mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Implement pretty debug format for AnyUserData similar to Value::UserData.
This commit is contained in:
+40
-1
@@ -711,7 +711,7 @@ pub trait UserData: Sized {
|
||||
///
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct AnyUserData(pub(crate) ValueRef);
|
||||
|
||||
impl AnyUserData {
|
||||
@@ -1081,6 +1081,45 @@ impl AnyUserData {
|
||||
};
|
||||
is_serializable().unwrap_or(false)
|
||||
}
|
||||
|
||||
unsafe fn invoke_tostring_dbg(&self) -> Result<Option<String>> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
// Try `__todebugstring` metamethod first, then `__tostring`
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__todebugstring")) == 0 {
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__tostring")) == 0 {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(lua.pop_value().as_string().map(|s| s.to_string_lossy()))
|
||||
}
|
||||
|
||||
pub(crate) fn fmt_pretty(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Try converting to a (debug) string first, with fallback to `__name/__type`
|
||||
match unsafe { self.invoke_tostring_dbg() } {
|
||||
Ok(Some(s)) => write!(fmt, "{s}"),
|
||||
_ => {
|
||||
let name = self.type_name().ok().flatten();
|
||||
let name = name.as_deref().unwrap_or("userdata");
|
||||
write!(fmt, "{name}: {:?}", self.to_pointer())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyUserData {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt);
|
||||
}
|
||||
fmt.debug_tuple("AnyUserData").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a [`AnyUserData`] metatable.
|
||||
|
||||
+1
-29
@@ -545,24 +545,6 @@ impl Value {
|
||||
ident: usize,
|
||||
visited: &mut HashSet<*const c_void>,
|
||||
) -> fmt::Result {
|
||||
unsafe fn invoke_tostring_dbg(vref: &ValueRef) -> Result<Option<String>> {
|
||||
let lua = vref.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(vref);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
// Try `__todebugstring` metamethod first, then `__tostring`
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__todebugstring")) == 0 {
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__tostring")) == 0 {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(lua.pop_value().as_string().map(|s| s.to_string_lossy()))
|
||||
}
|
||||
|
||||
match self {
|
||||
Value::Nil => write!(fmt, "nil"),
|
||||
Value::Boolean(b) => write!(fmt, "{b}"),
|
||||
@@ -579,17 +561,7 @@ impl Value {
|
||||
t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
|
||||
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
|
||||
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
|
||||
u @ Value::UserData(ud) => unsafe {
|
||||
// Try converting to a (debug) string first, with fallback to `__name/__type`
|
||||
match invoke_tostring_dbg(&ud.0) {
|
||||
Ok(Some(s)) => write!(fmt, "{s}"),
|
||||
_ => {
|
||||
let name = ud.type_name().ok().flatten();
|
||||
let name = name.as_deref().unwrap_or("userdata");
|
||||
write!(fmt, "{name}: {:?}", u.to_pointer())
|
||||
}
|
||||
}
|
||||
},
|
||||
Value::UserData(ud) => ud.fmt_pretty(fmt),
|
||||
#[cfg(feature = "luau")]
|
||||
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
|
||||
+9
-3
@@ -3,7 +3,8 @@ use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use mlua::{
|
||||
Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, UserDataRegistry, Value,
|
||||
AnyUserData, Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, UserDataRegistry,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -234,11 +235,16 @@ fn test_debug_format() -> Result<()> {
|
||||
struct ToStringUserData;
|
||||
impl UserData for ToStringUserData {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
registry.add_meta_method("__tostring", |_, _, ()| Ok("to-string-only"));
|
||||
registry.add_meta_method("__tostring", |_, _, ()| Ok("regular-string"));
|
||||
}
|
||||
}
|
||||
let tostring_only_ud = Value::UserData(lua.create_userdata(ToStringUserData)?);
|
||||
assert_eq!(format!("{tostring_only_ud:#?}"), "tostring-only");
|
||||
assert_eq!(format!("{tostring_only_ud:#?}"), "regular-string");
|
||||
|
||||
// Check that `AnyUsedata` pretty debug format is same as for `Value::UserData`
|
||||
let any_ud: AnyUserData = lua.create_userdata(ToDebugUserData)?;
|
||||
let value_ud = Value::UserData(any_ud.clone());
|
||||
assert_eq!(format!("{any_ud:#?}"), format!("{value_ud:#?}"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user