Add Lua::create_proxy for easy access to UserData static fields and functions
Closes #178
This commit is contained in:
parent
9af1aaf889
commit
e7f494530f
44
src/lua.rs
44
src/lua.rs
|
@ -29,7 +29,7 @@ use crate::types::{
|
|||
Number, RegistryKey,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataCell};
|
||||
use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods};
|
||||
use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods, UserDataProxy};
|
||||
use crate::util::{
|
||||
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
|
||||
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
|
||||
|
@ -1721,6 +1721,7 @@ impl Lua {
|
|||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
#[inline]
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData,
|
||||
|
@ -1733,6 +1734,7 @@ impl Lua {
|
|||
/// Requires `feature = "serialize"`
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[inline]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData + Serialize,
|
||||
|
@ -1740,6 +1742,46 @@ impl Lua {
|
|||
unsafe { self.make_userdata(UserDataCell::new_ser(data)) }
|
||||
}
|
||||
|
||||
/// Create a Lua userdata "proxy" object from a custom userdata type.
|
||||
///
|
||||
/// Proxy object is an empty userdata object that has `T` metatable attached.
|
||||
/// The main purpose of this object is to provide access to static fields and functions
|
||||
/// without creating an instance of type `T`.
|
||||
///
|
||||
/// You can get or set uservalues on this object but you cannot borrow any Rust type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// struct MyUserData(i32);
|
||||
///
|
||||
/// impl UserData for MyUserData {
|
||||
/// fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
/// }
|
||||
///
|
||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
/// methods.add_function("new", |_, value: i32| Ok(MyUserData(value)));
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// lua.globals().set("MyUserData", lua.create_proxy::<MyUserData>()?)?;
|
||||
///
|
||||
/// lua.load("assert(MyUserData.new(321).val == 321)").exec()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn create_proxy<T>(&self) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
unsafe { self.make_userdata(UserDataCell::new(UserDataProxy::<T>(PhantomData))) }
|
||||
}
|
||||
|
||||
/// Returns a handle to the global environment.
|
||||
pub fn globals(&self) -> Table {
|
||||
unsafe {
|
||||
|
|
|
@ -622,3 +622,8 @@ lua_userdata_impl!(Arc<RwLock<T>>);
|
|||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>>);
|
||||
#[cfg(feature = "parking_lot")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>>);
|
||||
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
|
|
|
@ -656,3 +656,44 @@ fn test_userdata_wrapped() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_proxy() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_function_get("static_field", |_, _| Ok(123));
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("new", |_, n| Ok(Self(n)));
|
||||
|
||||
methods.add_method("plus", |_, this, n: i64| Ok(this.0 + n));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("MyUserData", lua.create_proxy::<MyUserData>()?)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
assert(MyUserData.static_field == 123)
|
||||
local data = MyUserData.new(321)
|
||||
assert(data.static_field == 123)
|
||||
assert(data.n == 321)
|
||||
assert(data:plus(1) == 322)
|
||||
|
||||
-- Error when accessing the proxy object fields and methods that require instance
|
||||
|
||||
local ok = pcall(function() return MyUserData.n end)
|
||||
assert(not ok)
|
||||
|
||||
ok = pcall(function() return MyUserData:plus(1) end)
|
||||
assert(not ok)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue