mirror of
https://github.com/lune-org/lune.git
synced 2025-04-04 10:30:54 +01:00
Initial implementation of new value formatter
This commit is contained in:
parent
e983b60141
commit
3433fb4c4f
8 changed files with 252 additions and 3 deletions
|
@ -1,5 +1,8 @@
|
|||
mod components;
|
||||
mod stack_trace;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::components::ErrorComponents;
|
||||
pub use self::stack_trace::{StackTrace, StackTraceLine, StackTraceSource};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
mod error;
|
||||
mod label;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod value;
|
||||
|
||||
pub use self::error::{ErrorComponents, StackTrace, StackTraceLine, StackTraceSource};
|
||||
pub use self::label::Label;
|
||||
pub use self::value::{pretty_format_multi_value, pretty_format_value, ValueFormatConfig};
|
||||
|
|
73
crates/lune-utils/src/fmt/value/basic.rs
Normal file
73
crates/lune-utils/src/fmt/value/basic.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::{
|
||||
metamethods::{call_table_tostring_metamethod, call_userdata_tostring_metamethod},
|
||||
style::{COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_YELLOW},
|
||||
};
|
||||
|
||||
const STRING_REPLACEMENTS: &[(&str, &str)] =
|
||||
&[("\"", r#"\""#), ("\t", r"\t"), ("\r", r"\r"), ("\n", r"\n")];
|
||||
|
||||
/**
|
||||
Tries to return the given value as a plain string key.
|
||||
|
||||
A plain string key must:
|
||||
|
||||
- Start with an alphabetic character.
|
||||
- Only contain alphanumeric characters and underscores.
|
||||
*/
|
||||
pub(crate) fn lua_value_as_plain_string_key(value: &LuaValue) -> Option<String> {
|
||||
if let LuaValue::String(s) = value {
|
||||
if let Ok(s) = s.to_str() {
|
||||
let first_valid = s.chars().next().is_some_and(|c| c.is_ascii_alphabetic());
|
||||
let all_valid = s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
|
||||
if first_valid && all_valid {
|
||||
return Some(s.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a Lua value into a pretty string.
|
||||
|
||||
This does not recursively format tables.
|
||||
*/
|
||||
pub(crate) fn format_value_styled(value: &LuaValue) -> String {
|
||||
match value {
|
||||
LuaValue::Nil => COLOR_YELLOW.apply_to("nil").to_string(),
|
||||
LuaValue::Boolean(true) => COLOR_YELLOW.apply_to("true").to_string(),
|
||||
LuaValue::Boolean(false) => COLOR_YELLOW.apply_to("false").to_string(),
|
||||
LuaValue::Number(n) => COLOR_CYAN.apply_to(n).to_string(),
|
||||
LuaValue::Integer(i) => COLOR_CYAN.apply_to(i).to_string(),
|
||||
LuaValue::String(s) => COLOR_GREEN
|
||||
.apply_to({
|
||||
let mut s = s.to_string_lossy().to_string();
|
||||
for (from, to) in STRING_REPLACEMENTS {
|
||||
s = s.replace(from, to);
|
||||
}
|
||||
format!(r#""{s}""#)
|
||||
})
|
||||
.to_string(),
|
||||
LuaValue::Vector(_) => COLOR_MAGENTA.apply_to("<vector>").to_string(),
|
||||
LuaValue::Thread(_) => COLOR_MAGENTA.apply_to("<thread>").to_string(),
|
||||
LuaValue::Function(_) => COLOR_MAGENTA.apply_to("<function>").to_string(),
|
||||
LuaValue::LightUserData(_) => COLOR_MAGENTA.apply_to("<pointer>").to_string(),
|
||||
LuaValue::UserData(u) => {
|
||||
if let Some(s) = call_userdata_tostring_metamethod(u) {
|
||||
s
|
||||
} else {
|
||||
COLOR_MAGENTA.apply_to("<userdata>").to_string()
|
||||
}
|
||||
}
|
||||
LuaValue::Table(t) => {
|
||||
if let Some(s) = call_table_tostring_metamethod(t) {
|
||||
s
|
||||
} else {
|
||||
COLOR_MAGENTA.apply_to("<table>").to_string()
|
||||
}
|
||||
}
|
||||
_ => COLOR_MAGENTA.apply_to("<?>").to_string(),
|
||||
}
|
||||
}
|
48
crates/lune-utils/src/fmt/value/config.rs
Normal file
48
crates/lune-utils/src/fmt/value/config.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
Configuration for formatting values.
|
||||
*/
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ValueFormatConfig {
|
||||
pub(super) max_depth: usize,
|
||||
pub(super) colors_enabled: bool,
|
||||
}
|
||||
|
||||
impl ValueFormatConfig {
|
||||
/**
|
||||
Creates a new config with default values.
|
||||
*/
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the maximum depth to which tables will be formatted.
|
||||
*/
|
||||
#[must_use]
|
||||
pub fn with_max_depth(self, max_depth: usize) -> Self {
|
||||
Self { max_depth, ..self }
|
||||
}
|
||||
|
||||
/**
|
||||
Sets whether colors should be enabled.
|
||||
|
||||
Colors are disabled by default.
|
||||
*/
|
||||
#[must_use]
|
||||
pub fn with_colors_enabled(self, colors_enabled: bool) -> Self {
|
||||
Self {
|
||||
colors_enabled,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ValueFormatConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_depth: 3,
|
||||
colors_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
29
crates/lune-utils/src/fmt/value/metamethods.rs
Normal file
29
crates/lune-utils/src/fmt/value/metamethods.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
pub fn call_table_tostring_metamethod<'a>(tab: &'a LuaTable<'a>) -> Option<String> {
|
||||
let f = match tab.get_metatable() {
|
||||
None => None,
|
||||
Some(meta) => match meta.get::<_, LuaFunction>(LuaMetaMethod::ToString.name()) {
|
||||
Ok(method) => Some(method),
|
||||
Err(_) => None,
|
||||
},
|
||||
}?;
|
||||
match f.call::<_, String>(()) {
|
||||
Ok(res) => Some(res),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_userdata_tostring_metamethod<'a>(tab: &'a LuaAnyUserData<'a>) -> Option<String> {
|
||||
let f = match tab.get_metatable() {
|
||||
Err(_) => None,
|
||||
Ok(meta) => match meta.get::<LuaFunction>(LuaMetaMethod::ToString.name()) {
|
||||
Ok(method) => Some(method),
|
||||
Err(_) => None,
|
||||
},
|
||||
}?;
|
||||
match f.call::<_, String>(()) {
|
||||
Ok(res) => Some(res),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
88
crates/lune-utils/src/fmt/value/mod.rs
Normal file
88
crates/lune-utils/src/fmt/value/mod.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use std::fmt::{self, Write as _};
|
||||
|
||||
use console::{colors_enabled, set_colors_enabled};
|
||||
use mlua::prelude::*;
|
||||
|
||||
mod basic;
|
||||
mod config;
|
||||
mod metamethods;
|
||||
mod style;
|
||||
|
||||
pub use self::config::ValueFormatConfig;
|
||||
|
||||
use self::basic::{format_value_styled, lua_value_as_plain_string_key};
|
||||
use self::style::STYLE_DIM;
|
||||
|
||||
type FmtResult = Result<String, fmt::Error>;
|
||||
|
||||
fn format_value_inner(value: &LuaValue, config: &ValueFormatConfig, depth: usize) -> FmtResult {
|
||||
let mut buffer = String::new();
|
||||
|
||||
// TODO: Rewrite this section to not be recursive and
|
||||
// keep track of any recursive references to tables.
|
||||
if let LuaValue::Table(ref t) = value {
|
||||
if depth >= config.max_depth {
|
||||
write!(buffer, "{}", STYLE_DIM.apply_to("{ ... }"))?;
|
||||
} else {
|
||||
writeln!(buffer, "{}", STYLE_DIM.apply_to("{"))?;
|
||||
|
||||
for res in t.clone().pairs::<LuaValue, LuaValue>() {
|
||||
let (key, value) = res.expect("conversion to LuaValue should never fail");
|
||||
let formatted = if let Some(plain_key) = lua_value_as_plain_string_key(&key) {
|
||||
format!(
|
||||
"{} {} {}{}",
|
||||
plain_key,
|
||||
STYLE_DIM.apply_to("="),
|
||||
format_value_inner(&value, config, depth + 1)?,
|
||||
STYLE_DIM.apply_to(","),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}{}{} {} {}{}",
|
||||
STYLE_DIM.apply_to("["),
|
||||
format_value_inner(&key, config, depth + 1)?,
|
||||
STYLE_DIM.apply_to("]"),
|
||||
STYLE_DIM.apply_to("="),
|
||||
format_value_inner(&value, config, depth + 1)?,
|
||||
STYLE_DIM.apply_to(","),
|
||||
)
|
||||
};
|
||||
buffer.push_str(&formatted);
|
||||
}
|
||||
|
||||
writeln!(buffer, "{}", STYLE_DIM.apply_to("}"))?;
|
||||
}
|
||||
} else {
|
||||
write!(buffer, "{}", format_value_styled(value))?;
|
||||
}
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a Lua value into a pretty string using the given config.
|
||||
*/
|
||||
#[must_use]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn pretty_format_value(value: &LuaValue, config: &ValueFormatConfig) -> String {
|
||||
let colors_were_enabled = colors_enabled();
|
||||
set_colors_enabled(config.colors_enabled);
|
||||
let res = format_value_inner(value, config, 0);
|
||||
set_colors_enabled(colors_were_enabled);
|
||||
res.expect("using fmt for writing into strings should never fail")
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a Lua multi-value into a pretty string using the given config.
|
||||
|
||||
Each value will be separated by a space.
|
||||
*/
|
||||
#[must_use]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn pretty_format_multi_value(values: &LuaMultiValue, config: &ValueFormatConfig) -> String {
|
||||
values
|
||||
.into_iter()
|
||||
.map(|value| pretty_format_value(value, config))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
9
crates/lune-utils/src/fmt/value/style.rs
Normal file
9
crates/lune-utils/src/fmt/value/style.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use console::Style;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub static COLOR_GREEN: Lazy<Style> = Lazy::new(|| Style::new().green());
|
||||
pub static COLOR_YELLOW: Lazy<Style> = Lazy::new(|| Style::new().yellow());
|
||||
pub static COLOR_MAGENTA: Lazy<Style> = Lazy::new(|| Style::new().magenta());
|
||||
pub static COLOR_CYAN: Lazy<Style> = Lazy::new(|| Style::new().cyan());
|
||||
|
||||
pub static STYLE_DIM: Lazy<Style> = Lazy::new(|| Style::new().dim());
|
Loading…
Add table
Reference in a new issue