diff --git a/src/conversion.rs b/src/conversion.rs index 3317449..b7b37eb 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -149,14 +149,13 @@ impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T { impl<'lua, T: 'static + UserData> FromLua<'lua> for T { #[inline] - fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result { - match value { - Value::UserData(ud) => Ok(ud.clone_or_take::()?), - _ => Err(Error::FromLuaConversionError { - from: value.type_name(), - to: "userdata", - message: None, - }), + fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { + match &value { + Value::UserData(ud) => match ud.clone_or_take::() { + Ok(x) => Ok(x), + Err(_) => ::from_lua_fallback(value, lua), + }, + _ => ::from_lua_fallback(value, lua), } } } diff --git a/src/userdata.rs b/src/userdata.rs index 92f5ea7..af477b3 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -700,6 +700,17 @@ pub trait UserData: Sized { Self: 'static + MaybeSend, { } + /// Allows you to add extra conversion ways from Lua values to this type. + fn from_lua_fallback(lua_value: Value, _: &Lua) -> Result { + match lua_value { + Value::UserData(_) => Err(Error::UserDataTypeMismatch), + x => Err(Error::FromLuaConversionError { + from: x.type_name(), + to: "userdata", + message: None, + }), + } + } } // Wraps UserData in a way to always implement `serde::Serialize` trait. diff --git a/tests/userdata.rs b/tests/userdata.rs index 8dc89b6..a4df258 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -627,3 +627,35 @@ fn test_userdata_wrapped() -> Result<()> { Ok(()) } + +#[test] +fn test_from_lua_fallback() -> Result<()> { + struct MyUserData(i64); + impl UserData for MyUserData { + fn from_lua_fallback(value: Value, _: &Lua) -> hv_lua::Result { + match value { + Value::Integer(x) => Ok(MyUserData(x)), + Value::UserData(_) => Err(Error::UserDataTypeMismatch), + x => Err(Error::FromLuaConversionError { + from: x.type_name(), + to: "userdata", + message: None, + }), + } + } + } + let lua = Lua::new(); + let globals = lua.globals(); + globals.set("my_userdata", MyUserData(1))?; + let (MyUserData(first), MyUserData(second)) = lua + .load( + r#" + local to_return = 2 + return my_userdata, to_return + "#, + ) + .eval()?; + assert_eq!(1, first); + assert_eq!(2, second); + Ok(()) +}