mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 04:50:36 +00:00
Improve formatting / printing of userdata and tables with __type and / or __tostring metamethods
This commit is contained in:
parent
0efc2c565b
commit
1fb1d3e7b5
7 changed files with 121 additions and 50 deletions
|
@ -81,7 +81,7 @@ impl LuaUserData for LuaCaptures {
|
|||
|
||||
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.num_captures()));
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||
Ok(format!("RegexCaptures({})", this.num_captures()))
|
||||
Ok(format!("{}", this.num_captures()))
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ impl LuaUserData for LuaMatch {
|
|||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.range().len()));
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||
Ok(format!("RegexMatch({})", this.slice()))
|
||||
Ok(this.slice().to_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ impl LuaUserData for LuaRegex {
|
|||
);
|
||||
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||
Ok(format!("Regex({})", this.inner.as_str()))
|
||||
Ok(this.inner.as_str().to_string())
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use crate::fmt::ErrorComponents;
|
||||
|
||||
use super::{
|
||||
metamethods::{call_table_tostring_metamethod, call_userdata_tostring_metamethod},
|
||||
metamethods::{
|
||||
call_table_tostring_metamethod, call_userdata_tostring_metamethod,
|
||||
get_table_type_metavalue, get_userdata_type_metavalue,
|
||||
},
|
||||
style::{COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_YELLOW},
|
||||
};
|
||||
|
||||
|
@ -56,19 +61,39 @@ pub(crate) fn format_value_styled(value: &LuaValue, prefer_plain: bool) -> Strin
|
|||
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()
|
||||
}
|
||||
let formatted = format_typename_and_tostringed(
|
||||
"userdata",
|
||||
get_userdata_type_metavalue(u),
|
||||
call_userdata_tostring_metamethod(u),
|
||||
);
|
||||
COLOR_MAGENTA.apply_to(formatted).to_string()
|
||||
}
|
||||
LuaValue::Table(t) => {
|
||||
if let Some(s) = call_table_tostring_metamethod(t) {
|
||||
s
|
||||
} else {
|
||||
COLOR_MAGENTA.apply_to("<table>").to_string()
|
||||
let formatted = format_typename_and_tostringed(
|
||||
"table",
|
||||
get_table_type_metavalue(t),
|
||||
call_table_tostring_metamethod(t),
|
||||
);
|
||||
COLOR_MAGENTA.apply_to(formatted).to_string()
|
||||
}
|
||||
}
|
||||
_ => COLOR_MAGENTA.apply_to("<?>").to_string(),
|
||||
LuaValue::Error(e) => COLOR_MAGENTA
|
||||
.apply_to(format!(
|
||||
"<LuaError(\n{})>",
|
||||
ErrorComponents::from(e.clone())
|
||||
))
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_typename_and_tostringed(
|
||||
fallback: &'static str,
|
||||
typename: Option<String>,
|
||||
tostringed: Option<String>,
|
||||
) -> String {
|
||||
match (typename, tostringed) {
|
||||
(Some(typename), Some(tostringed)) => format!("<{typename}({tostringed})>"),
|
||||
(Some(typename), None) => format!("<{typename}>"),
|
||||
(None, Some(tostringed)) => format!("<{tostringed}>"),
|
||||
(None, None) => format!("<{fallback}>"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,37 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
pub fn get_table_type_metavalue<'a>(tab: &'a LuaTable<'a>) -> Option<String> {
|
||||
let s = tab
|
||||
.get_metatable()?
|
||||
.get::<_, LuaString>(LuaMetaMethod::Type.name())
|
||||
.ok()?;
|
||||
let s = s.to_str().ok()?;
|
||||
Some(s.to_string())
|
||||
}
|
||||
|
||||
pub fn get_userdata_type_metavalue<'a>(tab: &'a LuaAnyUserData<'a>) -> Option<String> {
|
||||
let s = tab
|
||||
.get_metatable()
|
||||
.ok()?
|
||||
.get::<LuaString>(LuaMetaMethod::Type.name())
|
||||
.ok()?;
|
||||
let s = s.to_str().ok()?;
|
||||
Some(s.to_string())
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
tab.get_metatable()?
|
||||
.get::<_, LuaFunction>(LuaMetaMethod::ToString.name())
|
||||
.ok()?
|
||||
.call(tab)
|
||||
.ok()
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
tab.get_metatable()
|
||||
.ok()?
|
||||
.get::<LuaFunction>(LuaMetaMethod::ToString.name())
|
||||
.ok()?
|
||||
.call(tab)
|
||||
.ok()
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
local regex = require("@lune/regex")
|
||||
|
||||
local re = regex.new("[0-9]+")
|
||||
assert(tostring(re) == "Regex([0-9]+)")
|
||||
assert(tostring(re) == "[0-9]+")
|
||||
assert(typeof(re) == "Regex")
|
||||
|
||||
local mtch = re:find("1337 wow")
|
||||
assert(tostring(mtch) == "RegexMatch(1337)")
|
||||
assert(tostring(mtch) == "1337")
|
||||
assert(typeof(mtch) == "RegexMatch")
|
||||
|
||||
local re2 = regex.new("([0-9]+) ([0-9]+) wow! ([0-9]+) ([0-9]+)")
|
||||
local captures = re2:captures("1337 125600 wow! 1984 0")
|
||||
assert(tostring(captures) == "RegexCaptures(4)")
|
||||
assert(tostring(captures) == "4")
|
||||
assert(typeof(captures) == "RegexCaptures")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local process = require("@lune/process")
|
||||
local regex = require("@lune/regex")
|
||||
local roblox = require("@lune/roblox")
|
||||
local stdio = require("@lune/stdio")
|
||||
|
||||
|
@ -9,6 +10,13 @@ local function assertFormatting(errorMessage: string, formatted: string, expecte
|
|||
end
|
||||
end
|
||||
|
||||
local function assertContains(errorMessage: string, haystack: string, needle: string)
|
||||
if string.find(haystack, needle) == nil then
|
||||
stdio.ewrite(string.format("%s\nHaystack: %s\nNeedle: %s", errorMessage, needle, haystack))
|
||||
process.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
assertFormatting(
|
||||
"Should add a single space between arguments",
|
||||
stdio.format("Hello", "world", "!"),
|
||||
|
@ -47,25 +55,38 @@ assertFormatting(
|
|||
|
||||
local userdatas = {
|
||||
Foo = newproxy(false),
|
||||
Bar = (roblox :: any).Vector3.new(),
|
||||
Bar = regex.new("TEST"),
|
||||
Baz = (roblox :: any).Vector3.new(1, 2, 3),
|
||||
}
|
||||
|
||||
assertFormatting(
|
||||
"Should format userdatas as their type (unknown userdata)",
|
||||
"Should format userdatas as generic 'userdata' if unknown",
|
||||
stdio.format(userdatas.Foo),
|
||||
"<userdata>"
|
||||
)
|
||||
|
||||
assertFormatting(
|
||||
"Should format userdatas as their type (known userdata)",
|
||||
assertContains(
|
||||
"Should format userdatas with their type if they have a __type metafield",
|
||||
stdio.format(userdatas.Bar),
|
||||
"<Vector3>"
|
||||
"Regex"
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"Should format userdatas with their type even if they have a __tostring metamethod",
|
||||
stdio.format(userdatas.Baz),
|
||||
"Vector3"
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"Should format userdatas with their tostringed value if they have a __tostring metamethod",
|
||||
stdio.format(userdatas.Baz),
|
||||
"1, 2, 3"
|
||||
)
|
||||
|
||||
assertFormatting(
|
||||
"Should format userdatas as their type in tables",
|
||||
"Should format userdatas properly in tables",
|
||||
stdio.format(userdatas),
|
||||
"{\n Foo = <userdata>,\n Bar = <Vector3>,\n}"
|
||||
"{\n Bar = <Regex(TEST)>,\n Baz = <Vector3(1, 2, 3)>,\n Foo = <userdata>,\n}"
|
||||
)
|
||||
|
||||
local nested = {
|
||||
|
@ -80,7 +101,24 @@ local nested = {
|
|||
},
|
||||
}
|
||||
|
||||
assert(
|
||||
string.find(stdio.format(nested), "Nesting = { ... }", 1, true) ~= nil,
|
||||
"Should print 4 levels of nested tables before cutting off"
|
||||
assertContains(
|
||||
"Should print 4 levels of nested tables before cutting off",
|
||||
stdio.format(nested),
|
||||
"Nesting = { ... }"
|
||||
)
|
||||
|
||||
local _, errorMessage = pcall(function()
|
||||
local function innerInnerFn()
|
||||
process.spawn("PROGRAM_THAT_DOES_NOT_EXIST")
|
||||
end
|
||||
local function innerFn()
|
||||
innerInnerFn()
|
||||
end
|
||||
innerFn()
|
||||
end)
|
||||
|
||||
stdio.ewrite(typeof(errorMessage))
|
||||
|
||||
assertContains("Should format errors similarly to userdata", stdio.format(errorMessage), "<LuaErr")
|
||||
assertContains("Should format errors with stack begins", stdio.format(errorMessage), "Stack Begin")
|
||||
assertContains("Should format errors with stack ends", stdio.format(errorMessage), "Stack End")
|
||||
|
|
Loading…
Reference in a new issue