Add `UserDataRef` and `UserDataRefMut` types that implement `FromLua`
and can be used as accessors to underlying `AnyUserData` type.
This commit is contained in:
parent
888bd77e60
commit
68b60e2a0a
|
@ -15,7 +15,7 @@ use crate::string::String;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::types::{LightUserData, MaybeSend};
|
use crate::types::{LightUserData, MaybeSend};
|
||||||
use crate::userdata::{AnyUserData, UserData};
|
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
|
||||||
use crate::value::{FromLua, IntoLua, Nil, Value};
|
use crate::value::{FromLua, IntoLua, Nil, Value};
|
||||||
|
|
||||||
#[cfg(feature = "unstable")]
|
#[cfg(feature = "unstable")]
|
||||||
|
@ -217,6 +217,20 @@ impl<'lua, T: 'static + MaybeSend + UserData> IntoLua<'lua> for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> FromLua<'lua> for UserDataRef<'lua, T> {
|
||||||
|
#[inline]
|
||||||
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
|
Self::from_value(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> FromLua<'lua> for UserDataRefMut<'lua, T> {
|
||||||
|
#[inline]
|
||||||
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
|
Self::from_value(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua> IntoLua<'lua> for Error {
|
impl<'lua> IntoLua<'lua> for Error {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
|
|
@ -121,6 +121,7 @@ pub use crate::thread::{Thread, ThreadStatus};
|
||||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||||
pub use crate::userdata::{
|
pub use crate::userdata::{
|
||||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||||
|
UserDataRef, UserDataRefMut,
|
||||||
};
|
};
|
||||||
pub use crate::userdata_ext::AnyUserDataExt;
|
pub use crate::userdata_ext::AnyUserDataExt;
|
||||||
pub use crate::userdata_impl::UserDataRegistrar;
|
pub use crate::userdata_impl::UserDataRegistrar;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::any::TypeId;
|
use std::any::{type_name, TypeId};
|
||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
use std::string::String as StdString;
|
use std::string::String as StdString;
|
||||||
|
@ -22,7 +23,7 @@ use crate::lua::Lua;
|
||||||
use crate::table::{Table, TablePairs};
|
use crate::table::{Table, TablePairs};
|
||||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use crate::types::AsyncCallback;
|
use crate::types::AsyncCallback;
|
||||||
|
@ -1218,6 +1219,67 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper type for an immutably borrowed value from a `AnyUserData`.
|
||||||
|
///
|
||||||
|
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||||
|
pub struct UserDataRef<'lua, T: 'static>(AnyUserData<'lua>, Ref<'lua, T>);
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> Deref for UserDataRef<'lua, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> UserDataRef<'lua, T> {
|
||||||
|
pub(crate) fn from_value(value: Value<'lua>) -> Result<Self> {
|
||||||
|
let ud = try_value_to_userdata::<T>(value)?;
|
||||||
|
// It's safe to lift lifetime of `Ref<T>` to `'lua` as long as we hold AnyUserData to it.
|
||||||
|
let this = unsafe { mem::transmute(ud.borrow::<T>()?) };
|
||||||
|
Ok(UserDataRef(ud, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||||
|
///
|
||||||
|
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||||
|
pub struct UserDataRefMut<'lua, T: 'static>(AnyUserData<'lua>, RefMut<'lua, T>);
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> Deref for UserDataRefMut<'lua, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> DerefMut for UserDataRefMut<'lua, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: 'static> UserDataRefMut<'lua, T> {
|
||||||
|
pub(crate) fn from_value(value: Value<'lua>) -> Result<Self> {
|
||||||
|
let ud = try_value_to_userdata::<T>(value)?;
|
||||||
|
// It's safe to lift lifetime of `RefMut<T>` to `'lua` as long as we hold AnyUserData to it.
|
||||||
|
let this = unsafe { mem::transmute(ud.borrow_mut::<T>()?) };
|
||||||
|
Ok(UserDataRefMut(ud, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||||
|
match value {
|
||||||
|
Value::UserData(ud) => Ok(ud),
|
||||||
|
_ => Err(Error::FromLuaConversionError {
|
||||||
|
from: value.type_name(),
|
||||||
|
to: "userdata",
|
||||||
|
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod assertions {
|
mod assertions {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -13,8 +13,8 @@ use std::{cell::RefCell, rc::Rc};
|
||||||
use std::sync::atomic::{AtomicI64, Ordering};
|
use std::sync::atomic::{AtomicI64, Ordering};
|
||||||
|
|
||||||
use mlua::{
|
use mlua::{
|
||||||
AnyUserData, AnyUserDataExt, Error, ExternalError, FromLua, Function, Lua, MetaMethod, Nil,
|
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result,
|
||||||
Result, String, UserData, UserDataFields, UserDataMethods, Value,
|
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -96,29 +96,25 @@ fn test_metamethods() -> Result<()> {
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct MyUserData(i64);
|
struct MyUserData(i64);
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for MyUserData {
|
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
|
||||||
match value {
|
|
||||||
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserData for MyUserData {
|
impl UserData for MyUserData {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_method("get", |_, data, ()| Ok(data.0));
|
methods.add_method("get", |_, data, ()| Ok(data.0));
|
||||||
methods.add_meta_function(
|
methods.add_meta_function(
|
||||||
MetaMethod::Add,
|
MetaMethod::Add,
|
||||||
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 + rhs.0)),
|
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
|
||||||
|
Ok(MyUserData(lhs.0 + rhs.0))
|
||||||
|
},
|
||||||
);
|
);
|
||||||
methods.add_meta_function(
|
methods.add_meta_function(
|
||||||
MetaMethod::Sub,
|
MetaMethod::Sub,
|
||||||
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 - rhs.0)),
|
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
|
||||||
|
Ok(MyUserData(lhs.0 - rhs.0))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
methods.add_meta_function(
|
||||||
|
MetaMethod::Eq,
|
||||||
|
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| Ok(lhs.0 == rhs.0),
|
||||||
);
|
);
|
||||||
methods.add_meta_function(MetaMethod::Eq, |_, (lhs, rhs): (MyUserData, MyUserData)| {
|
|
||||||
Ok(lhs.0 == rhs.0)
|
|
||||||
});
|
|
||||||
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
|
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
|
||||||
if index.to_str()? == "inner" {
|
if index.to_str()? == "inner" {
|
||||||
Ok(data.0)
|
Ok(data.0)
|
||||||
|
@ -134,7 +130,8 @@ fn test_metamethods() -> Result<()> {
|
||||||
))]
|
))]
|
||||||
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
|
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
|
let stateless_iter =
|
||||||
|
lua.create_function(|_, (data, i): (UserDataRef<Self>, i64)| {
|
||||||
let i = i + 1;
|
let i = i + 1;
|
||||||
if i <= data.0 {
|
if i <= data.0 {
|
||||||
return Ok(mlua::Variadic::from_iter(vec![i, i]));
|
return Ok(mlua::Variadic::from_iter(vec![i, i]));
|
||||||
|
@ -152,7 +149,9 @@ fn test_metamethods() -> Result<()> {
|
||||||
globals.set("userdata2", MyUserData(3))?;
|
globals.set("userdata2", MyUserData(3))?;
|
||||||
globals.set("userdata3", MyUserData(3))?;
|
globals.set("userdata3", MyUserData(3))?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
|
lua.load("userdata1 + userdata2")
|
||||||
|
.eval::<UserDataRef<MyUserData>>()?
|
||||||
|
.0,
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -176,7 +175,12 @@ fn test_metamethods() -> Result<()> {
|
||||||
)
|
)
|
||||||
.eval::<Function>()?;
|
.eval::<Function>()?;
|
||||||
|
|
||||||
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
|
assert_eq!(
|
||||||
|
lua.load("userdata1 - userdata2")
|
||||||
|
.eval::<UserDataRef<MyUserData>>()?
|
||||||
|
.0,
|
||||||
|
4
|
||||||
|
);
|
||||||
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
|
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
|
||||||
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
||||||
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
||||||
|
|
Loading…
Reference in New Issue