Code cleanup

This commit is contained in:
SeanOMik 2024-02-11 09:59:28 -05:00
parent 6e787af0f0
commit b0902967d1
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
3 changed files with 399 additions and 565 deletions

View File

@ -1,80 +1,48 @@
use std::ffi::CStr; use std::{cell::{Ref, RefCell, RefMut}, ffi::CStr};
use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State, Userdata};
use mlua_sys as lua; use mlua_sys as lua;
pub struct UnsafeUserdata<'a> { use crate::{ensure_type, AsLua, FromLua, FromLuaStack, Function, IntoLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Table, Userdata, Value};
/// A handle to some userdata on the stack
//#[derive(Clone)]
pub struct AnyUserdata<'a> {
pub(crate) lref: LuaRef<'a>, pub(crate) lref: LuaRef<'a>,
state: &'a State, state: &'a State,
} }
impl<'a> Clone for UnsafeUserdata<'a> { impl<'a> Clone for AnyUserdata<'a> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
lref: self.lref.clone(),
state: self.state, state: self.state,
lref: self.lref.clone()
} }
} }
} }
impl<'a> UnsafeUserdata<'a> { impl<'a> AnyUserdata<'a> {
pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self { pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self {
Self { Self {
lref,
state, state,
lref,
} }
} }
/// Returns a borrow to the userdata. /// Returns a borrow to the userdata.
pub fn as_ref<T: Userdata + 'static>(&self) -> crate::Result<&'a T> { pub fn as_ref<T: Userdata + 'static>(&self) -> crate::Result<Ref<'a, T>> {
unsafe { unsafe {
self.state.ensure_stack(3)?; let cell = self.as_ptr::<T>()?;
let _g = StackGuard::new(self.state); let cell = cell.as_ref().unwrap();
let s = self.state.state_ptr(); Ok(cell.borrow())
self.lref.push_to_lua_stack(self.state)?;
if lua::lua_getmetatable(s, -1) == 0 {
return Err(crate::Error::UserdataMismatch);
}
self.state.get_userdata_metatable::<T>()
.push_to_lua_stack(self.state)?;
if lua::lua_rawequal(s, -2, -1) == 1 {
let cptr = lua::lua_touserdata(s, -3);
let t = &*cptr.cast::<T>();
Ok(t)
} else {
return Err(crate::Error::UserdataMismatch);
}
} }
} }
/// Returns a mutable reference to the userdata. /// Returns a mutable reference to the userdata.
pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<&'a mut T> { pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<RefMut<'a, T>> {
unsafe { unsafe {
self.state.ensure_stack(3)?; let cell = self.as_ptr::<T>()?;
let _g = StackGuard::new(self.state); let cell = cell.as_mut().unwrap();
let s = self.state.state_ptr(); Ok(cell.borrow_mut())
self.lref.push_to_lua_stack(self.state)?;
if lua::lua_getmetatable(s, -1) == 0 {
return Err(crate::Error::MissingMetatable);
}
self.state.get_userdata_metatable::<T>()
.push_to_lua_stack(self.state)?;
if lua::lua_rawequal(s, -2, -1) == 1 {
let cptr = lua::lua_touserdata(s, -3);
let t = &mut *cptr.cast::<T>();
Ok(t)
} else {
Err(crate::Error::UserdataMismatch)
}
} }
} }
@ -84,9 +52,9 @@ impl<'a> UnsafeUserdata<'a> {
/// * You must be certain that the type `T` is the same type that this userdata has a handle to. /// * You must be certain that the type `T` is the same type that this userdata has a handle to.
/// There is a blind cast from `void*` to `T*` /// There is a blind cast from `void*` to `T*`
/// ///
/// If there is a possibility that these types do not match, use [`UnsafeUserdata::as_ptr`] /// If there is a possibility that these types do not match, use [`AnyUserdata::as_ptr`]
/// which does verify the types. /// which does verify the types.
pub unsafe fn as_ptr_unchecked<T>(&self) -> crate::Result<*mut T> { pub unsafe fn as_ptr_unchecked<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<T>> {
self.state.ensure_stack(1)?; self.state.ensure_stack(1)?;
let _g = StackGuard::new(self.state); let _g = StackGuard::new(self.state);
let s = self.state.state_ptr(); let s = self.state.state_ptr();
@ -97,11 +65,11 @@ impl<'a> UnsafeUserdata<'a> {
Ok(cptr.cast()) Ok(cptr.cast())
} }
/// Returns a mutable pointer of the data stored. /// Returns a mutable pointer of the [`RefCell`] storing the userdata.
/// ///
/// This function ensures that the type of the userdata this struct has a handle to is the /// This function ensures that the type of the userdata this struct has a handle to is the
/// same as `T`. If it isn't, a `UserdataMismatch` error will be returned. /// same as `T`. If it isn't, a `UserdataMismatch` error will be returned.
pub unsafe fn as_ptr<T: Userdata + 'static>(&self) -> crate::Result<*mut T> { pub unsafe fn as_ptr<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<T>> {
let _g = StackGuard::new(self.state); let _g = StackGuard::new(self.state);
let s = self.state.state_ptr(); let s = self.state.state_ptr();
@ -148,33 +116,108 @@ impl<'a> UnsafeUserdata<'a> {
Ok(cstr.to_string()) Ok(cstr.to_string())
} }
} }
/// Returns the metatable of this userdata
pub fn get_metatable(&'a self) -> crate::Result<Table<'a>> {
unsafe {
self.state.ensure_stack(2)?;
let _g = StackGuard::new(self.state);
//self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
self.push_to_lua_stack(self.state)?;
let s = self.state.state_ptr();
if lua::lua_getmetatable(s, -1) == 0 {
Err(crate::Error::MissingMetatable)
} else {
let mut t = Table::from_lua_stack(self.state)?;
t.mt_marker = true;
Ok(t)
}
}
} }
impl<'a> FromLuaStack<'a> for UnsafeUserdata<'a> { /// Gets something from the userdata.
///
/// Will trigger a call to the `__index` metamethod. Use [`AnyUserdata::raw_get`] if you
/// don't want to call it.
pub fn get<K, V>(&'a self, key: K) -> crate::Result<V>
where
K: AsLua<'a>,
V: FromLua<'a>,
{
let mt = self.get_metatable()?;
mt.get::<K, V>(key)
}
/// Gets something from the userdata. Will **not** trigger a call to the `__index` metamethod.
pub fn raw_get<K, V>(&'a self, key: K) -> crate::Result<V>
where
K: AsLua<'a>,
V: FromLua<'a>,
{
let mt = self.get_metatable()?;
mt.raw_get::<K, V>(key)
}
/// Execute a method on this userdata. This finds a function with `name` and executes it
/// with the userdata as the first argument.
pub fn execute_method<A, R>(&'a self, name: &str, args: A) -> crate::Result<R>
where
A: IntoLuaVec<'a>,
R: FromLua<'a>,
{
let name = name.to_string();
let mt = self.get_metatable()?;
let f = mt.get::<_, Function>(name)?;
let mut args = args.into_lua_value_vec(self.state)?;
args.push_front(self.clone().as_lua(self.state)?);
f.exec(args)
}
}
impl<'a> FromLuaStack<'a> for AnyUserdata<'a> {
unsafe fn from_lua_stack(state: &'a State) -> crate::Result<Self> { unsafe fn from_lua_stack(state: &'a State) -> crate::Result<Self> {
ensure_type(state, lua::LUA_TUSERDATA, -1)?; ensure_type(state, lua::LUA_TUSERDATA, -1)?;
Ok(UnsafeUserdata { Ok(AnyUserdata {
lref: LuaRef::from_stack(state)?,
state, state,
lref: LuaRef::from_stack(state)?,
}) })
} }
} }
impl<'a> PushToLuaStack<'a> for UnsafeUserdata<'a> { impl<'a> PushToLuaStack<'a> for AnyUserdata<'a> {
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
self.lref.push_to_lua_stack(state) self.lref.push_to_lua_stack(state)
} }
} }
/* impl<'a> AsLua<'a> for UnsafeUserdata<'a> { impl<'a> AsLua<'a> for AnyUserdata<'a> {
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> { fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
Ok(Value::Userdata(self.clone())) Ok(Value::Userdata(self.clone()))
} }
} }
impl<'a> FromLua<'a> for UnsafeUserdata<'a> { impl<'a> FromLua<'a> for AnyUserdata<'a> {
fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result<Self> { fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result<Self> {
val.into_userdata() val.into_userdata()
} }
} */ }
impl<'a, T: Userdata + 'static> FromLua<'a> for Ref<'a, T> {
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
let ud = val.into_userdata()?;
ud.as_ref::<T>()
}
}
impl<'a, T: Userdata + 'static> FromLua<'a> for RefMut<'a, T> {
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
let ud = val.into_userdata()?;
ud.as_mut::<T>()
}
}

283
src/userdata/builder.rs Executable file
View File

@ -0,0 +1,283 @@
use std::{cell::OnceCell, collections::HashMap, marker::PhantomData, mem, ops::DerefMut, sync::Arc};
use crate::{AnyUserdata, AsLua, FromLua, FromLuaVec, State, Userdata, Value, ValueVec};
pub type UserdataFn<'a> = Box<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
pub type UserdataGetterFn<'a> = Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>;
pub type UserdataMutGetterFn<'a> = Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*mut ()>>>>;
pub struct UserdataBuilder<'a, T> {
pub(crate) name: String,
pub(crate) field_getters: HashMap<String, UserdataFn<'a>>,
pub(crate) field_setters: HashMap<String, UserdataFn<'a>>,
pub(crate) functions: HashMap<String, UserdataFn<'a>>,
pub(crate) meta_methods: HashMap<String, UserdataFn<'a>>,
pub(crate) wrapped_getter: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>,
pub(crate) wrapped_getter_mut: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*mut ()>>>>,
_marker: PhantomData<T>,
}
impl<'a, T: Userdata> UserdataBuilder<'a, T> {
pub fn new() -> Self {
Self {
name: T::name(),
field_getters: HashMap::new(),
field_setters: HashMap::new(),
functions: HashMap::new(),
meta_methods: HashMap::new(),
wrapped_getter: Arc::new(OnceCell::new()),
wrapped_getter_mut: Arc::new(OnceCell::new()),
_marker: PhantomData,
}
}
pub fn field_getter<F, R>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &T) -> crate::Result<R> + 'static,
R: AsLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let val = val.pop_front().unwrap();
let this = val.as_userdata().unwrap(); // if this panics, its a bug
if let Some(getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { &*this_ptr };
f(lua, this).and_then(|r| r.as_lua(lua))
} else {
let this = this.as_ref::<T>()?;
f(lua, &*this).and_then(|r| r.as_lua(lua))
}
};
self.field_getters.insert(name.to_string(), Box::new(wrap));
self
}
pub fn field_setter<F, V>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &mut T, V) -> () + 'static,
V: FromLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter_mut.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*mut (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let lua_val = val.pop_front().unwrap();
let this = lua_val.as_userdata().unwrap(); // if this panics, its a bug
let lua_val = val.pop_front().unwrap();
let v_arg = V::from_lua(lua, lua_val)?;
if let Some(mut_getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
mut_getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { this_ptr.as_mut().unwrap() };
f(lua, this, v_arg).as_lua(lua)
} else {
let mut this = this.as_mut::<T>()?;
f(lua, this.deref_mut(), v_arg).as_lua(lua)
}
};
self.field_setters.insert(name.to_string(), Box::new(wrap));
self
}
fn arg_result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str) -> crate::Result<R> {
res.map_err(|e| match e {
crate::Error::ValueVecError { value_idx, error } => {
let full_name = format!("{}.{}", ud_name, fn_name);
// added 2 to value index since the error idx starts at 0,
// and `this` counts as the first argument in the lua function.
crate::Error::BadArgument { func: Some(full_name), arg_index: value_idx + 2, arg_name: None, error, }
},
_ => e
})
}
fn result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str, arg_idx: i32, arg_name: Option<&str>) -> crate::Result<R> {
res.map_err(|e| {
let full_name = format!("{}.{}", ud_name, fn_name);
let arg_name = arg_name.map(|s| s.to_string());
crate::Error::BadArgument { func: Some(full_name), arg_index: arg_idx, arg_name, error: Arc::new(e), }
})
}
pub fn function<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
{
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, val: ValueVec<'a>| {
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
f(lua, args).and_then(|r| r.as_lua(lua))
};
self.functions.insert(name.to_string(), Box::new(wrap));
self
}
pub fn method<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let this_val = val.pop_front().unwrap();
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
if let Some(mut_getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
mut_getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { this_ptr.as_ref().unwrap() };
f(lua, this, args).and_then(|r| r.as_lua(lua))
} else {
let this = this.as_ref::<T>()?;
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
}
};
self.functions.insert(name.to_string(), Box::new(wrap));
self
}
pub fn method_mut<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &mut T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter_mut.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*mut (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let this_val = val.pop_front().unwrap();
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
if let Some(mut_getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
mut_getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { this_ptr.as_mut().unwrap() };
f(lua, this, args).and_then(|r| r.as_lua(lua))
} else {
let mut this = this.as_mut::<T>()?;
f(lua, this.deref_mut(), args).and_then(|r| r.as_lua(lua))
}
};
self.functions.insert(name.to_string(), Box::new(wrap));
self
}
pub fn meta_method<N, F, R, A>(&mut self, name: N, f: F) -> &mut Self
where
N: AsRef<str>,
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
T: Userdata + 'static
{
let ud_name = self.name.clone();
let fn_name = name.as_ref().to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let this_val = val.pop_front().unwrap();
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
let this = this.as_ref::<T>()?;
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
};
self.meta_methods.insert(name.as_ref().to_string(), Box::new(wrap));
self
}
//fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
pub fn expand_with<U>(&mut self, other: UserdataBuilder<'a, U>) {
self.field_getters = other.field_getters;
self.field_setters = other.field_setters;
self.functions = other.functions;
self.meta_methods = other.meta_methods;
self.wrapped_getter = other.wrapped_getter;
self.wrapped_getter_mut = other.wrapped_getter_mut;
}
}

View File

@ -1,27 +1,26 @@
use std::{cell::{OnceCell, Ref, RefCell, RefMut}, collections::HashMap, ffi::CStr, marker::PhantomData, mem, ops::DerefMut, sync::Arc}; use crate::{AsLua, PushToLuaStack, State, Value};
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, Function, IntoLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Table, Value, ValueVec}; pub mod any;
#[allow(unused_imports)]
use mlua_sys as lua; pub use any::*;
pub mod unsafe_ud;
pub use unsafe_ud::*;
pub mod proxy; pub mod proxy;
#[allow(unused_imports)] #[allow(unused_imports)]
use proxy::*; pub use proxy::*;
pub mod builder;
#[allow(unused_imports)]
pub use builder::*;
pub mod borrow; pub mod borrow;
#[allow(unused_imports)] #[allow(unused_imports)]
use borrow::*; pub use borrow::*;
pub mod borrow_mut; pub mod borrow_mut;
#[allow(unused_imports)] #[allow(unused_imports)]
use borrow_mut::*; pub use borrow_mut::*;
/// An enum representing Lua meta methods and fields
/// An enum representing all Lua MetaMethods
/// https://gist.github.com/oatmealine/655c9e64599d0f0dd47687c1186de99f
pub enum MetaMethod { pub enum MetaMethod {
Add, Add,
Sub, Sub,
@ -105,499 +104,8 @@ impl<'a> PushToLuaStack<'a> for MetaMethod {
} }
} }
pub type UserdataFn<'a> = Box<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
pub type UserdataGetterFn<'a> = Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>;
pub type UserdataMutGetterFn<'a> = Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*mut ()>>>>;
pub struct UserdataBuilder<'a, T> {
pub(crate) name: String,
pub(crate) field_getters: HashMap<String, UserdataFn<'a>>,
pub(crate) field_setters: HashMap<String, UserdataFn<'a>>,
pub(crate) functions: HashMap<String, UserdataFn<'a>>,
pub(crate) meta_methods: HashMap<String, UserdataFn<'a>>,
pub(crate) wrapped_getter: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>,
pub(crate) wrapped_getter_mut: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*mut ()>>>>,
_marker: PhantomData<T>,
}
impl<'a, T: Userdata> UserdataBuilder<'a, T> {
pub fn new() -> Self {
Self {
name: T::name(),
field_getters: HashMap::new(),
field_setters: HashMap::new(),
functions: HashMap::new(),
meta_methods: HashMap::new(),
wrapped_getter: Arc::new(OnceCell::new()),
wrapped_getter_mut: Arc::new(OnceCell::new()),
_marker: PhantomData,
}
}
pub fn field_getter<F, R>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &T) -> crate::Result<R> + 'static,
R: AsLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let val = val.pop_front().unwrap();
let this = val.as_userdata().unwrap(); // if this panics, its a bug
if let Some(getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { &*this_ptr };
f(lua, this).and_then(|r| r.as_lua(lua))
} else {
let this = this.as_ref::<T>()?;
f(lua, &*this).and_then(|r| r.as_lua(lua))
}
};
self.field_getters.insert(name.to_string(), Box::new(wrap));
self
}
pub fn field_setter<F, V>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &mut T, V) -> () + 'static,
V: FromLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter_mut.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*mut (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let lua_val = val.pop_front().unwrap();
let this = lua_val.as_userdata().unwrap(); // if this panics, its a bug
let lua_val = val.pop_front().unwrap();
let v_arg = V::from_lua(lua, lua_val)?;
if let Some(mut_getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
mut_getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { this_ptr.as_mut().unwrap() };
f(lua, this, v_arg).as_lua(lua)
} else {
let mut this = this.as_mut::<T>()?;
f(lua, this.deref_mut(), v_arg).as_lua(lua)
}
};
self.field_setters.insert(name.to_string(), Box::new(wrap));
self
}
fn arg_result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str) -> crate::Result<R> {
res.map_err(|e| match e {
crate::Error::ValueVecError { value_idx, error } => {
let full_name = format!("{}.{}", ud_name, fn_name);
// added 2 to value index since the error idx starts at 0,
// and `this` counts as the first argument in the lua function.
crate::Error::BadArgument { func: Some(full_name), arg_index: value_idx + 2, arg_name: None, error, }
},
_ => e
})
}
fn result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str, arg_idx: i32, arg_name: Option<&str>) -> crate::Result<R> {
res.map_err(|e| {
let full_name = format!("{}.{}", ud_name, fn_name);
let arg_name = arg_name.map(|s| s.to_string());
crate::Error::BadArgument { func: Some(full_name), arg_index: arg_idx, arg_name, error: Arc::new(e), }
})
}
pub fn function<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
{
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, val: ValueVec<'a>| {
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
f(lua, args).and_then(|r| r.as_lua(lua))
};
self.functions.insert(name.to_string(), Box::new(wrap));
self
}
pub fn method<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let this_val = val.pop_front().unwrap();
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
if let Some(mut_getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
mut_getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { this_ptr.as_ref().unwrap() };
f(lua, this, args).and_then(|r| r.as_lua(lua))
} else {
let this = this.as_ref::<T>()?;
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
}
};
self.functions.insert(name.to_string(), Box::new(wrap));
self
}
pub fn method_mut<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &mut T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
T: Userdata + 'static
{
let wrapped = self.wrapped_getter_mut.clone();
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*mut (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
let ud_name = self.name.clone();
let fn_name = name.to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let this_val = val.pop_front().unwrap();
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
if let Some(mut_getter) = wrapped.get() {
let this_ptr = Self::result_to_bad_arg(
mut_getter(this.clone()),
&ud_name,
&fn_name,
1,
Some("self")
)?;
let this_ptr = this_ptr.cast::<T>();
let this = unsafe { this_ptr.as_mut().unwrap() };
f(lua, this, args).and_then(|r| r.as_lua(lua))
} else {
let mut this = this.as_mut::<T>()?;
f(lua, this.deref_mut(), args).and_then(|r| r.as_lua(lua))
}
};
self.functions.insert(name.to_string(), Box::new(wrap));
self
}
pub fn meta_method<N, F, R, A>(&mut self, name: N, f: F) -> &mut Self
where
N: AsRef<str>,
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a>,
R: AsLua<'a>,
T: Userdata + 'static
{
let ud_name = self.name.clone();
let fn_name = name.as_ref().to_string();
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
let this_val = val.pop_front().unwrap();
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
let this = this.as_ref::<T>()?;
let args = Self::arg_result_to_bad_arg(
A::from_lua_value_vec(lua, val),
&ud_name, &fn_name
)?;
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
};
self.meta_methods.insert(name.as_ref().to_string(), Box::new(wrap));
self
}
//fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
pub fn expand_with<U>(&mut self, other: UserdataBuilder<'a, U>) {
self.field_getters = other.field_getters;
self.field_setters = other.field_setters;
self.functions = other.functions;
self.meta_methods = other.meta_methods;
self.wrapped_getter = other.wrapped_getter;
self.wrapped_getter_mut = other.wrapped_getter_mut;
}
}
pub trait Userdata: Sized { pub trait Userdata: Sized {
fn name() -> String; fn name() -> String;
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>; fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>;
} }
/// A handle to some userdata on the stack
//#[derive(Clone)]
pub struct AnyUserdata<'a> {
pub(crate) unsafe_ud: UnsafeUserdata<'a>,
state: &'a State,
}
impl<'a> Clone for AnyUserdata<'a> {
fn clone(&self) -> Self {
Self {
state: self.state,
unsafe_ud: self.unsafe_ud.clone()
}
}
}
impl<'a> AnyUserdata<'a> {
pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self {
Self {
state,
unsafe_ud: UnsafeUserdata::from_ref(state, lref)
}
}
/// Returns a borrow to the userdata.
pub fn as_ref<T: Userdata + 'static>(&self) -> crate::Result<Ref<'a, T>> {
unsafe {
let cell = &*self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
Ok(cell.borrow())
}
}
/// Returns a mutable reference to the userdata.
pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<RefMut<'a, T>> {
unsafe {
let cell = &*self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
Ok(cell.borrow_mut())
}
}
/// Returns a mutable pointer of the [`RefCell`] of userdata **WITHOUT verifying the type of it**.
///
/// # Safety
/// * You must be certain that the type `T` is the same type that this userdata has a handle to.
/// There is a blind cast from `void*` to `T*`
///
/// If there is a possibility that these types do not match, use [`AnyUserdata::as_ptr`]
/// which does verify the types.
pub unsafe fn as_ptr_unchecked<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<T>> {
let cell = self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
Ok(cell)
}
/// Returns a mutable pointer of the [`RefCell`] storing the userdata.
///
/// This function ensures that the type of the userdata this struct has a handle to is the
/// same as `T`. If it isn't, a `UserdataMismatch` error will be returned.
pub unsafe fn as_ptr<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<T>> {
let _g = StackGuard::new(self.state);
let s = self.state.state_ptr();
self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
if lua::lua_getmetatable(s, -1) == 0 {
return Err(crate::Error::MissingMetatable);
}
self.state.get_userdata_metatable::<T>()
.push_to_lua_stack(self.state)?;
if lua::lua_rawequal(s, -2, -1) == 1 {
drop(_g);
let cell = self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
Ok(cell)
} else {
Err(crate::Error::UserdataMismatch)
}
}
/// Returns the name of the userdata by accessing the metatable
pub fn name(&self) -> crate::Result<String> {
unsafe {
self.state.ensure_stack(3)?;
let _g = StackGuard::new(self.state);
self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
let s = self.state.state_ptr();
if lua::lua_getmetatable(s, -1) == 0 {
return Err(crate::Error::MissingMetatable);
}
lua::lua_pushliteral(s, "__name");
lua::lua_rawget(s, -2);
ensure_type(self.state, lua::LUA_TSTRING, -1)?;
let cstr = CStr::from_ptr(lua::lua_tostring(s, -1));
let cstr = cstr.to_str()
// on panic, this should be considered a bug
.expect("Metatable name has invalid utf8 bytes!");
Ok(cstr.to_string())
}
}
/// Returns the metatable of this userdata
pub fn get_metatable(&'a self) -> crate::Result<Table<'a>> {
unsafe {
self.state.ensure_stack(2)?;
let _g = StackGuard::new(self.state);
//self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
self.push_to_lua_stack(self.state)?;
let s = self.state.state_ptr();
if lua::lua_getmetatable(s, -1) == 0 {
Err(crate::Error::MissingMetatable)
} else {
let mut t = Table::from_lua_stack(self.state)?;
t.mt_marker = true;
Ok(t)
}
}
}
/// Gets something from the userdata.
///
/// Will trigger a call to the `__index` metamethod. Use [`AnyUserdata::raw_get`] if you
/// don't want to call it.
pub fn get<K, V>(&'a self, key: K) -> crate::Result<V>
where
K: AsLua<'a>,
V: FromLua<'a>,
{
let mt = self.get_metatable()?;
mt.get::<K, V>(key)
}
/// Gets something from the userdata. Will **not** trigger a call to the `__index` metamethod.
pub fn raw_get<K, V>(&'a self, key: K) -> crate::Result<V>
where
K: AsLua<'a>,
V: FromLua<'a>,
{
let mt = self.get_metatable()?;
mt.raw_get::<K, V>(key)
}
/// Execute a method on this userdata. This finds a function with `name` and executes it
/// with the userdata as the first argument.
pub fn execute_method<A, R>(&'a self, name: &str, args: A) -> crate::Result<R>
where
A: IntoLuaVec<'a>,
R: FromLua<'a>,
{
let name = name.to_string();
let mt = self.get_metatable()?;
let f = mt.get::<_, Function>(name)?;
let mut args = args.into_lua_value_vec(self.state)?;
args.push_front(self.clone().as_lua(self.state)?);
f.exec(args)
}
}
impl<'a> FromLuaStack<'a> for AnyUserdata<'a> {
unsafe fn from_lua_stack(state: &'a State) -> crate::Result<Self> {
ensure_type(state, lua::LUA_TUSERDATA, -1)?;
Ok(AnyUserdata {
state,
unsafe_ud: UnsafeUserdata::from_lua_stack(state)?,
})
}
}
impl<'a> PushToLuaStack<'a> for AnyUserdata<'a> {
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
self.unsafe_ud.lref.push_to_lua_stack(state)
}
}
impl<'a> AsLua<'a> for AnyUserdata<'a> {
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
Ok(Value::Userdata(self.clone()))
}
}
impl<'a> FromLua<'a> for AnyUserdata<'a> {
fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result<Self> {
val.into_userdata()
}
}
impl<'a, T: Userdata + 'static> FromLua<'a> for Ref<'a, T> {
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
let ud = val.into_userdata()?;
ud.as_ref::<T>()
}
}
impl<'a, T: Userdata + 'static> FromLua<'a> for RefMut<'a, T> {
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
let ud = val.into_userdata()?;
ud.as_mut::<T>()
}
}