Skip to content

Commit 151adc0

Browse files
committed
Implement pretty debug format for AnyUserData similar to Value::UserData.
1 parent 7f3ec63 commit 151adc0

File tree

3 files changed

+50
-33
lines changed

3 files changed

+50
-33
lines changed

src/userdata.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ pub trait UserData: Sized {
711711
///
712712
/// [`is`]: crate::AnyUserData::is
713713
/// [`borrow`]: crate::AnyUserData::borrow
714-
#[derive(Clone, Debug, PartialEq)]
714+
#[derive(Clone, PartialEq)]
715715
pub struct AnyUserData(pub(crate) ValueRef);
716716

717717
impl AnyUserData {
@@ -1081,6 +1081,45 @@ impl AnyUserData {
10811081
};
10821082
is_serializable().unwrap_or(false)
10831083
}
1084+
1085+
unsafe fn invoke_tostring_dbg(&self) -> Result<Option<String>> {
1086+
let lua = self.0.lua.lock();
1087+
let state = lua.state();
1088+
let _guard = StackGuard::new(state);
1089+
check_stack(state, 3)?;
1090+
1091+
lua.push_ref(&self.0);
1092+
protect_lua!(state, 1, 1, fn(state) {
1093+
// Try `__todebugstring` metamethod first, then `__tostring`
1094+
if ffi::luaL_callmeta(state, -1, cstr!("__todebugstring")) == 0 {
1095+
if ffi::luaL_callmeta(state, -1, cstr!("__tostring")) == 0 {
1096+
ffi::lua_pushnil(state);
1097+
}
1098+
}
1099+
})?;
1100+
Ok(lua.pop_value().as_string().map(|s| s.to_string_lossy()))
1101+
}
1102+
1103+
pub(crate) fn fmt_pretty(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1104+
// Try converting to a (debug) string first, with fallback to `__name/__type`
1105+
match unsafe { self.invoke_tostring_dbg() } {
1106+
Ok(Some(s)) => write!(fmt, "{s}"),
1107+
_ => {
1108+
let name = self.type_name().ok().flatten();
1109+
let name = name.as_deref().unwrap_or("userdata");
1110+
write!(fmt, "{name}: {:?}", self.to_pointer())
1111+
}
1112+
}
1113+
}
1114+
}
1115+
1116+
impl fmt::Debug for AnyUserData {
1117+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1118+
if fmt.alternate() {
1119+
return self.fmt_pretty(fmt);
1120+
}
1121+
fmt.debug_tuple("AnyUserData").field(&self.0).finish()
1122+
}
10841123
}
10851124

10861125
/// Handle to a [`AnyUserData`] metatable.

src/value.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -545,24 +545,6 @@ impl Value {
545545
ident: usize,
546546
visited: &mut HashSet<*const c_void>,
547547
) -> fmt::Result {
548-
unsafe fn invoke_tostring_dbg(vref: &ValueRef) -> Result<Option<String>> {
549-
let lua = vref.lua.lock();
550-
let state = lua.state();
551-
let _guard = StackGuard::new(state);
552-
check_stack(state, 3)?;
553-
554-
lua.push_ref(vref);
555-
protect_lua!(state, 1, 1, fn(state) {
556-
// Try `__todebugstring` metamethod first, then `__tostring`
557-
if ffi::luaL_callmeta(state, -1, cstr!("__todebugstring")) == 0 {
558-
if ffi::luaL_callmeta(state, -1, cstr!("__tostring")) == 0 {
559-
ffi::lua_pushnil(state);
560-
}
561-
}
562-
})?;
563-
Ok(lua.pop_value().as_string().map(|s| s.to_string_lossy()))
564-
}
565-
566548
match self {
567549
Value::Nil => write!(fmt, "nil"),
568550
Value::Boolean(b) => write!(fmt, "{b}"),
@@ -579,17 +561,7 @@ impl Value {
579561
t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
580562
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
581563
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
582-
u @ Value::UserData(ud) => unsafe {
583-
// Try converting to a (debug) string first, with fallback to `__name/__type`
584-
match invoke_tostring_dbg(&ud.0) {
585-
Ok(Some(s)) => write!(fmt, "{s}"),
586-
_ => {
587-
let name = ud.type_name().ok().flatten();
588-
let name = name.as_deref().unwrap_or("userdata");
589-
write!(fmt, "{name}: {:?}", u.to_pointer())
590-
}
591-
}
592-
},
564+
Value::UserData(ud) => ud.fmt_pretty(fmt),
593565
#[cfg(feature = "luau")]
594566
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
595567
Value::Error(e) if recursive => write!(fmt, "{e:?}"),

tests/value.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::os::raw::c_void;
33
use std::ptr;
44

55
use mlua::{
6-
Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, UserDataRegistry, Value,
6+
AnyUserData, Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, UserDataRegistry,
7+
Value,
78
};
89

910
#[test]
@@ -234,11 +235,16 @@ fn test_debug_format() -> Result<()> {
234235
struct ToStringUserData;
235236
impl UserData for ToStringUserData {
236237
fn register(registry: &mut UserDataRegistry<Self>) {
237-
registry.add_meta_method("__tostring", |_, _, ()| Ok("to-string-only"));
238+
registry.add_meta_method("__tostring", |_, _, ()| Ok("regular-string"));
238239
}
239240
}
240241
let tostring_only_ud = Value::UserData(lua.create_userdata(ToStringUserData)?);
241-
assert_eq!(format!("{tostring_only_ud:#?}"), "tostring-only");
242+
assert_eq!(format!("{tostring_only_ud:#?}"), "regular-string");
243+
244+
// Check that `AnyUsedata` pretty debug format is same as for `Value::UserData`
245+
let any_ud: AnyUserData = lua.create_userdata(ToDebugUserData)?;
246+
let value_ud = Value::UserData(any_ud.clone());
247+
assert_eq!(format!("{any_ud:#?}"), format!("{value_ud:#?}"));
242248

243249
Ok(())
244250
}

0 commit comments

Comments
 (0)