Include (known) userdata type to Value debug pretty-print

This commit is contained in:
Alex Orlenko 2023-05-27 23:57:43 +01:00
parent 3d7796de55
commit b674d7906d
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
4 changed files with 63 additions and 16 deletions

View File

@ -19,6 +19,7 @@ use {
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
@ -150,7 +151,7 @@ impl PartialEq<MetaMethod> for &str {
}
}
impl PartialEq<MetaMethod> for String {
impl PartialEq<MetaMethod> for StdString {
fn eq(&self, other: &MetaMethod) -> bool {
self == other.name()
}
@ -411,18 +412,23 @@ pub trait UserDataMethods<'lua, T> {
//
#[doc(hidden)]
fn add_callback(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
fn add_callback(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
#[doc(hidden)]
#[cfg(feature = "async")]
fn add_async_callback(&mut self, _name: String, _callback: AsyncCallback<'lua, 'static>) {}
fn add_async_callback(&mut self, _name: StdString, _callback: AsyncCallback<'lua, 'static>) {}
#[doc(hidden)]
fn add_meta_callback(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
fn add_meta_callback(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
#[doc(hidden)]
#[cfg(feature = "async")]
fn add_async_meta_callback(&mut self, _name: String, _callback: AsyncCallback<'lua, 'static>) {}
fn add_async_meta_callback(
&mut self,
_name: StdString,
_callback: AsyncCallback<'lua, 'static>,
) {
}
}
/// Field registry for [`UserData`] implementors.
@ -495,10 +501,10 @@ pub trait UserDataFields<'lua, T> {
//
#[doc(hidden)]
fn add_field_getter(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
fn add_field_getter(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
#[doc(hidden)]
fn add_field_setter(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
fn add_field_setter(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
}
/// Trait for custom userdata types.
@ -1045,6 +1051,30 @@ impl<'lua> AnyUserData<'lua> {
OwnedAnyUserData(self.0.into_owned())
}
/// Returns a type name of this `UserData` (from `__name` metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_userdata_ref(&self.0)?;
let protect = !lua.unlikely_memory_error();
let name_type = if protect {
protect_lua!(state, 1, 1, |state| {
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
})?
} else {
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
};
match name_type {
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
_ => Ok(None),
}
}
}
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
// Uses lua_rawequal() under the hood
@ -1220,7 +1250,7 @@ impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
where
V: FromLua<'lua>,
{
type Item = Result<(String, V)>;
type Item = Result<(StdString, V)>;
fn next(&mut self) -> Option<Self::Item> {
loop {

View File

@ -214,7 +214,7 @@ impl<'lua> Value<'lua> {
Value::Nil => write!(fmt, "nil"),
Value::Boolean(b) => write!(fmt, "{b}"),
Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
Value::LightUserData(ud) => write!(fmt, "<lightuserdata {:?}>", ud.0),
Value::LightUserData(ud) => write!(fmt, "lightuserdata: {:?}", ud.0),
Value::Integer(i) => write!(fmt, "{i}"),
Value::Number(n) => write!(fmt, "{n}"),
#[cfg(feature = "luau")]
@ -223,13 +223,16 @@ impl<'lua> Value<'lua> {
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
t.fmt_pretty(fmt, ident, visited)
}
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()),
// TODO: Show type name for registered userdata
u @ Value::UserData(_) => write!(fmt, "<userdata {:?}>", u.to_pointer()),
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) => {
let name = ud.type_name().ok().flatten();
let name = name.unwrap_or_else(|| "userdata".to_string());
write!(fmt, "{name}: {:?}", u.to_pointer())
}
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
Value::Error(_) => write!(fmt, "<error>"),
Value::Error(_) => write!(fmt, "error"),
}
}
}

View File

@ -7,7 +7,7 @@ fn test_debug_format() -> Result<()> {
// Globals
let globals = lua.globals();
let dump = format!("{globals:#?}");
assert!(dump.starts_with("{\n [\"_G\"] = <table"));
assert!(dump.starts_with("{\n [\"_G\"] = table:"));
// TODO: Other cases

View File

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::os::raw::c_void;
use std::ptr;
use std::string::String as StdString;
@ -139,3 +140,16 @@ fn test_value_to_string() -> Result<()> {
Ok(())
}
#[test]
fn test_debug_format() -> Result<()> {
let lua = Lua::new();
lua.register_userdata_type::<HashMap<i32, StdString>>(|_| {})?;
let ud = lua
.create_any_userdata::<HashMap<i32, StdString>>(HashMap::new())
.map(Value::UserData)?;
assert!(format!("{ud:#?}").starts_with("HashMap<i32, String>:"));
Ok(())
}