diff --git a/Cargo.lock b/Cargo.lock index 294125a..60cb745 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,16 +17,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cstr" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aa998c33a6d3271e3678950a22134cd7dd27cef86dee1b611b5b14207d1d90b" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "libc" version = "0.2.152" @@ -37,7 +27,6 @@ checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" name = "lua-ffi" version = "0.1.0" dependencies = [ - "cstr", "mlua-sys", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 034dde8..cff1cf8 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cstr = "0.2.11" mlua-sys = { version = "0.5.0", features = ["lua54"] } thiserror = "1.0.56" diff --git a/src/lib.rs b/src/lib.rs new file mode 100755 index 0000000..3a395df --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,200 @@ +use mlua_sys as lua; + +pub mod state; +use state::*; + +pub mod table; +use table::*; + +pub mod function; +use function::*; + +pub mod value; +use value::*; + +pub mod guard; +use guard::*; + +pub mod userdata; +use userdata::*; + +pub mod util; +use util::*; + +pub mod chunk; +use chunk::*; + +pub mod error; +use error::*; + +pub mod lref; +use lref::*; + +#[cfg(test)] +pub mod tests; + +/* fn main() -> Result<()> { + let lua = State::new(); + lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); + + let globals = lua.globals()?; + + let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 }); + let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?; + globals.set("v1", ud)?; + + let v2 = Vec2 { x: 10.0, y: 15.0 }; + let ud = lua.create_userdata(UserdataRef::from(&v2))?; + globals.set("v2", ud)?; + + //lua.gc_stop()?; + let chunk = lua.load( + "text.lua", + r#" + require "util" + + --if vec2 ~= nil then + print("v1: (" .. v1.x .. ", " .. v1.y .. ")") + --print("v2: (" .. v2.x .. ", " .. v2.y .. ")") + --end + + --print("vec2.x = " .. vec2.x) + --print("vec2.y = " .. vec2.y) + "#, + )?; + + const MAX_RUNS: i32 = 400; + for i in 0..MAX_RUNS { + // I don't care about the result of this execution, so I set the result as a + // Value which can be anything + // + // Chunks can also be executed with: `chunk.execute(())?;` + let res = lua.execute_chunk::<_, Value>(&chunk, ()); + + // if unwrapped, the new lines in the message would be escaped making + // the traceback in the error difficult to read. + if let Err(e) = res { + panic!("{}", e); + } + + //print_refs(&lua).unwrap(); + println!("i = {}", i); + globals.set("v1", Value::Nil)?; + lua.gc_collect()?; + + let mut t = v1.borrow_mut(); + t.x += 50.0; + t.y += 5.0; + drop(t); + + let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?; + globals.raw_set("v1", ud)?; + } + + unsafe { + assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack + } + + Ok(()) +} */ + +pub trait PushToLuaStack<'a> { + unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>; +} + +pub trait FromLuaStack<'a>: Sized { + unsafe fn from_lua_stack(state: &'a State) -> Result; +} + +impl<'a> PushToLuaStack<'a> for () { + unsafe fn push_to_lua_stack(&self, _state: &'a State) -> Result<()> { + Ok(()) + } +} + +impl<'a, T: PushToLuaStack<'a>> PushToLuaStack<'a> for Option { + unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()> { + if let Some(v) = self { + v.push_to_lua_stack(state)?; + } else { + unsafe { + lua::lua_pushnil(state.state_ptr()); + } + } + + Ok(()) + } +} + +/// Implements PushToLuaStack for a number +macro_rules! impl_as_lua_number { + ($ty: ident) => { + impl<'a> AsLua<'a> for $ty { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { + Ok(Value::Number(*self as f64)) + } + } + + impl<'a> FromLua<'a> for $ty { + fn from_lua(_lua: &State, val: Value) -> crate::Result { + match val { + Value::Number(v) => Ok(v as $ty), + _ => Err(Error::UnexpectedType( + "Number".to_string(), + val.type_name().to_string(), + )), + } + } + } + + impl<'a> PushToLuaStack<'a> for $ty { + unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { + let v = self.as_lua(state)?; + v.push_to_lua_stack(state) + } + } + }; +} + +impl_as_lua_number!(i8); +impl_as_lua_number!(i16); +impl_as_lua_number!(i32); +impl_as_lua_number!(i64); + +impl_as_lua_number!(u8); +impl_as_lua_number!(u16); +impl_as_lua_number!(u32); +impl_as_lua_number!(u64); + +impl_as_lua_number!(f32); +impl_as_lua_number!(f64); + +impl<'a> PushToLuaStack<'a> for String { + unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { + state.ensure_stack(1)?; + + self.as_str() + .push_to_lua_stack(state)?; + + Ok(()) + } +} + +impl<'a> FromLua<'a> for String { + fn from_lua(_lua: &State, val: Value) -> crate::Result { + val.as_string().cloned() + } +} + +impl<'a> PushToLuaStack<'a> for &str { + unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { + state.ensure_stack(1)?; + + let s = format!("{}\0", self); + let cstr = s.as_ptr() as *const i8; + // lua copies the string, so its okay if the string pointer is dropped + lua::lua_pushstring(state.state_ptr(), cstr); + + Ok(()) + } +} \ No newline at end of file diff --git a/src/lref.rs b/src/lref.rs new file mode 100755 index 0000000..65abdb6 --- /dev/null +++ b/src/lref.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; + +use crate::{Error, PushToLuaStack, Result, State}; + +use mlua_sys as lua; + +/// A LuaRef is a a handle to something in Lua. +/// +/// These are created with `luaL_ref`, they can be created from anything on the top of the stack +/// with [`LuaRef::from_stack`]. The references are automatically freed with `luaL_unref` when +/// the inner Arc detects a single strong count. +#[derive(Clone)] +pub struct LuaRef<'a> { + lref: Arc, + state: &'a State, +} + +impl<'a> Drop for LuaRef<'a> { + fn drop(&mut self) { + unsafe { + let s = self.state.state_ptr(); + + if Arc::strong_count(&self.lref) == 1 { + lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, *self.lref); + } + } + } +} + +impl<'a> LuaRef<'a> { + pub fn new(lua_ref: i32, state: &'a State) -> Self { + Self { + lref: Arc::new(lua_ref), + state, + } + } + + /// Creates a reference to what is at the top of the stack. + pub unsafe fn from_stack(state: &'a State) -> Result { + let s = state.state_ptr(); + let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX); + if r == lua::LUA_REFNIL { + Err(Error::Nil) + } else { + Ok(LuaRef::new(r, state)) + } + } +} + +impl<'a> PushToLuaStack<'a> for LuaRef<'a> { + unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { + let s = state.state_ptr(); + + state.ensure_stack(1)?; + let top = lua::lua_gettop(s); + let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.lref as i64); + let new_top = lua::lua_gettop(s); + + if ty == lua::LUA_TNIL || ty == lua::LUA_TNONE || top == new_top { + return Err(Error::Nil); + } + + Ok(()) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs deleted file mode 100755 index 2f390da..0000000 --- a/src/main.rs +++ /dev/null @@ -1,463 +0,0 @@ -use std::{ - cell::{Ref, RefCell}, ffi::CStr, marker::PhantomData, mem, ops::Deref, sync::Arc -}; - -use mlua_sys as lua; - -pub mod state; -use state::*; - -pub mod table; -use table::*; - -pub mod function; -use function::*; - -pub mod value; -use value::*; - -pub mod guard; -use guard::*; - -pub mod userdata; -use userdata::*; - -pub mod util; -use util::*; - -pub mod chunk; -use chunk::*; - -pub mod error; -use error::*; - -fn main() -> Result<()> { - let lua = State::new(); - lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); - - let globals = lua.globals()?; - - let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 }); - let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?; - globals.set("v1", ud)?; - - let v2 = Vec2 { x: 10.0, y: 15.0 }; - let ud = lua.create_userdata(UserdataRef::from(&v2))?; - globals.set("v2", ud)?; - - //lua.gc_stop()?; - let chunk = lua.load( - "text.lua", - r#" - require "util" - - --if vec2 ~= nil then - print("v1: (" .. v1.x .. ", " .. v1.y .. ")") - --print("v2: (" .. v2.x .. ", " .. v2.y .. ")") - --end - - --print("vec2.x = " .. vec2.x) - --print("vec2.y = " .. vec2.y) - "#, - )?; - - const MAX_RUNS: i32 = 400; - for i in 0..MAX_RUNS { - // I don't care about the result of this execution, so I set the result as a - // Value which can be anything - // - // Chunks can also be executed with: `chunk.execute(())?;` - let res = lua.execute_chunk::<_, Value>(&chunk, ()); - - // if unwrapped, the new lines in the message would be escaped making - // the traceback in the error difficult to read. - if let Err(e) = res { - panic!("{}", e); - } - - //print_refs(&lua).unwrap(); - println!("i = {}", i); - globals.set("v1", Value::Nil)?; - lua.gc_collect()?; - - let mut t = v1.borrow_mut(); - t.x += 50.0; - t.y += 5.0; - drop(t); - - let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?; - globals.raw_set("v1", ud)?; - } - - unsafe { - assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack - } - - Ok(()) -} - -/// Prints the types of the registry -pub fn print_refs(lua: &State) -> Result<()> { - unsafe { - let _g = StackGuard::new(&lua); - let s = lua.state_ptr(); - - Value::String(String::from("Hello World")).push_to_lua_stack(&lua)?; - let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX); - - for i in 0..r { - let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, i as i64); - - match ty { - lua::LUA_TUSERDATA => { - lua::lua_getmetatable(s, -1); - lua::lua_pushliteral(s, "__name"); - lua::lua_rawget(s, -2); - - let v = Value::from_lua_stack(&lua)?; - println!("{}: {}", i, v.as_string().unwrap()); - }, - _ => { - let tyname = CStr::from_ptr(lua::lua_typename(s, ty)); - let tyname = tyname.to_str().unwrap(); - println!("{}: {}", i, tyname); - } - } - - lua::lua_pop(s, 1); - } - lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, r); - } - Ok(()) -} - -/// A LuaRef is a a handle to something in Lua. -/// -/// These are created with `luaL_ref`, they can be created from anything on the top of the stack -/// with [`LuaRef::from_stack`]. The references are automatically freed with `luaL_unref` when -/// the inner Arc detects a single strong count. -#[derive(Clone)] -pub struct LuaRef<'a> { - lref: Arc, - state: &'a State, -} - -impl<'a> Drop for LuaRef<'a> { - fn drop(&mut self) { - unsafe { - let s = self.state.state_ptr(); - - if Arc::strong_count(&self.lref) == 1 { - lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, *self.lref); - } - } - } -} - -impl<'a> LuaRef<'a> { - pub fn new(lua_ref: i32, state: &'a State) -> Self { - Self { - lref: Arc::new(lua_ref), - state, - } - } - - /// Creates a reference to what is at the top of the stack. - pub unsafe fn from_stack(state: &'a State) -> Result { - let s = state.state_ptr(); - let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX); - if r == lua::LUA_REFNIL { - Err(Error::Nil) - } else { - Ok(LuaRef::new(r, state)) - } - } -} - -impl<'a> PushToLuaStack<'a> for LuaRef<'a> { - unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { - let s = state.state_ptr(); - - state.ensure_stack(1)?; - let top = lua::lua_gettop(s); - let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.lref as i64); - let new_top = lua::lua_gettop(s); - - if ty == lua::LUA_TNIL || ty == lua::LUA_TNONE || top == new_top { - return Err(Error::Nil); - } - - Ok(()) - } -} - -pub trait PushToLuaStack<'a> { - unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>; -} - -pub trait FromLuaStack<'a>: Sized { - unsafe fn from_lua_stack(state: &'a State) -> Result; -} - -impl<'a> PushToLuaStack<'a> for () { - unsafe fn push_to_lua_stack(&self, _state: &'a State) -> Result<()> { - Ok(()) - } -} - -impl<'a, T: PushToLuaStack<'a>> PushToLuaStack<'a> for Option { - unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()> { - if let Some(v) = self { - v.push_to_lua_stack(state)?; - } else { - unsafe { - lua::lua_pushnil(state.state_ptr()); - } - } - - Ok(()) - } -} - -/// Implements PushToLuaStack for a number -macro_rules! impl_as_lua_number { - ($ty: ident) => { - impl<'a> AsLua<'a> for $ty { - fn as_lua(&self, _lua: &'a State) -> crate::Result> { - Ok(Value::Number(*self as f64)) - } - } - - impl<'a> FromLua<'a> for $ty { - fn from_lua(_lua: &State, val: Value) -> crate::Result { - match val { - Value::Number(v) => Ok(v as $ty), - _ => Err(Error::UnexpectedType( - "Number".to_string(), - val.type_name().to_string(), - )), - } - } - } - - impl<'a> PushToLuaStack<'a> for $ty { - unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { - let v = self.as_lua(state)?; - v.push_to_lua_stack(state) - } - } - }; -} - -impl_as_lua_number!(i8); -impl_as_lua_number!(i16); -impl_as_lua_number!(i32); -impl_as_lua_number!(i64); - -impl_as_lua_number!(u8); -impl_as_lua_number!(u16); -impl_as_lua_number!(u32); -impl_as_lua_number!(u64); - -impl_as_lua_number!(f32); -impl_as_lua_number!(f64); - -impl<'a> PushToLuaStack<'a> for String { - unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { - state.ensure_stack(1)?; - - let s = format!("{}\0", self); - let cstr = s.as_ptr() as *const i8; - lua::lua_pushstring(state.state_ptr(), cstr); - - Ok(()) - } -} - -impl<'a> FromLua<'a> for String { - fn from_lua(_lua: &State, val: Value) -> crate::Result { - val.as_string().cloned() - } -} - -impl<'a> PushToLuaStack<'a> for &str { - unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { - state.ensure_stack(1)?; - - let s = format!("{}\0", self); - let cstr = s.as_ptr() as *const i8; - lua::lua_pushstring(state.state_ptr(), cstr); - - Ok(()) - } -} - -#[allow(dead_code)] -pub struct Vec3 { - x: f32, - y: f32, - z: f32, -} - -impl Userdata for Vec3 { - fn build<'a>(_state: &State, _builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { - todo!() - } - - fn name() -> String { - todo!() - } -} - -pub struct Vec2 { - x: f32, - y: f32, -} - -impl Userdata for Vec2 { - fn build<'a>(_state: &State, builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { - builder - .field_getter("x", |_, this| Ok(this.x)) - .field_getter("y", |_, this| Ok(this.y)) - .field_setter("x", |_, this, x: f32| this.x = x) - .field_setter("y", |_, this, y: f32| this.y = y) - .function("new", |lua, (x, y)| lua.create_userdata(Vec2 { x, y })) - // method test - .method("add", |lua, lhs: &Vec2, (rhs,): (Ref,)| { - let lx = lhs.x; - let ly = lhs.y; - - let rx = rhs.x; - let ry = rhs.y; - - lua.create_userdata(Vec2 { - x: lx + rx, - y: ly + ry, - }) - }) - .meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (Ref,)| { - let lx = lhs.x; - let ly = lhs.y; - - let rx = rhs.x; - let ry = rhs.y; - - lua.create_userdata(Vec2 { - x: lx + rx, - y: ly + ry, - }) - }); - - Ok(()) - } - - fn name() -> String { - "Vec2".to_string() - } -} - -pub struct UserdataProxy(PhantomData); - -impl UserdataProxy { - pub fn new() -> Self { - Self(PhantomData) - } -} - -impl<'a, T: Userdata> AsLua<'a> for UserdataProxy { - fn as_lua(&self, _lua: &'a State) -> crate::Result> { - todo!() - } -} - -impl Userdata for UserdataProxy { - fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { - let mut other = UserdataBuilder::::new(); - T::build(state, &mut other)?; - - // only the functions need to be added since they're the only thing usable from a proxy - builder.functions = other.functions; - - Ok(()) - } - - fn name() -> String { - let name = format!("{}Proxy", T::name()); - name - } -} - -pub enum Borrow<'a, T> { - Wrapped(Ref<'a, T>), - Raw(&'a T), -} - -impl<'a, T> Deref for Borrow<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - match self { - Borrow::Wrapped(w) => w, - Borrow::Raw(w) => w, - } - } -} - -pub struct UserdataRef<'a, T: Userdata> { - ud_ptr: Borrow<'a, T>, -} - -impl<'a, T: Userdata> From<&'a T> for UserdataRef<'static, T> { - fn from(value: &'a T) -> Self { - let ud = Borrow::Raw(value); - - Self { - ud_ptr: unsafe { - mem::transmute(ud) - } - } - } -} - -impl<'a, T: Userdata> From> for UserdataRef<'static, T> { - fn from(value: Ref<'a, T>) -> Self { - let ud = Borrow::Wrapped(value); - - Self { - ud_ptr: unsafe { - mem::transmute(ud) - }, - } - } -} - -impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> { - fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> { - let mut other = UserdataBuilder::::new(); - T::build(state, &mut other)?; - - builder.expand_with(other); - - let getter: fn(AnyUserdata<'_>) -> Result<*const ()> = move |ud: AnyUserdata| { - let ud_ptr = { - let ud = ud.as_ref::>()?; - - let ud_ptr: *const T = &*ud.ud_ptr; - ud_ptr - }; - - Ok(ud_ptr.cast::<()>()) - }; - - if builder.wrapped_getter.set(Box::new(getter)).is_err() { - panic!("Somehow the wrapped getter has already been set"); - } - - Ok(()) - } - - fn name() -> String { - let name = format!("{}Ref", T::name()); - name - } -} diff --git a/src/tests.rs b/src/tests.rs new file mode 100755 index 0000000..c63feb5 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,50 @@ +use std::cell::Ref; + +use crate::{MetaMethod, State, Userdata, UserdataBuilder}; + +pub struct Vec2 { + x: f32, + y: f32, +} + +impl Userdata for Vec2 { + fn build<'a>(_state: &State, builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { + builder + .field_getter("x", |_, this| Ok(this.x)) + .field_getter("y", |_, this| Ok(this.y)) + .field_setter("x", |_, this, x: f32| this.x = x) + .field_setter("y", |_, this, y: f32| this.y = y) + .function("new", |lua, (x, y)| lua.create_userdata(Vec2 { x, y })) + // method test + .method("add", |lua, lhs: &Vec2, (rhs,): (Ref,)| { + let lx = lhs.x; + let ly = lhs.y; + + let rx = rhs.x; + let ry = rhs.y; + + lua.create_userdata(Vec2 { + x: lx + rx, + y: ly + ry, + }) + }) + .meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (Ref,)| { + let lx = lhs.x; + let ly = lhs.y; + + let rx = rhs.x; + let ry = rhs.y; + + lua.create_userdata(Vec2 { + x: lx + rx, + y: ly + ry, + }) + }); + + Ok(()) + } + + fn name() -> String { + "Vec2".to_string() + } +} \ No newline at end of file diff --git a/src/userdata/borrow.rs b/src/userdata/borrow.rs new file mode 100755 index 0000000..b49b784 --- /dev/null +++ b/src/userdata/borrow.rs @@ -0,0 +1,78 @@ +use std::{cell::Ref, mem, ops::Deref}; + +use crate::{AnyUserdata, Result, State, Userdata, UserdataBuilder}; + +enum Borrow<'a, T> { + Wrapped(Ref<'a, T>), + Raw(&'a T), +} + +impl<'a, T> Deref for Borrow<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + Borrow::Wrapped(w) => w, + Borrow::Raw(w) => w, + } + } +} + +pub struct UserdataRef<'a, T: Userdata> { + borrow: Borrow<'a, T>, +} + +impl<'a, T: Userdata> From<&'a T> for UserdataRef<'static, T> { + fn from(value: &'a T) -> Self { + let ud = Borrow::Raw(value); + + Self { + borrow: unsafe { + mem::transmute(ud) + } + } + } +} + +impl<'a, T: Userdata> From> for UserdataRef<'static, T> { + fn from(value: Ref<'a, T>) -> Self { + let ud = Borrow::Wrapped(value); + + Self { + borrow: unsafe { + mem::transmute(ud) + }, + } + } +} + +impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> { + fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> { + let mut other = UserdataBuilder::::new(); + T::build(state, &mut other)?; + + builder.expand_with(other); + + let getter: fn(AnyUserdata<'_>) -> Result<*const ()> = move |ud: AnyUserdata| { + let ud_ptr = { + let ud = ud.as_ref::>()?; + + let ud_ptr: *const T = &*ud.borrow; + ud_ptr + }; + + Ok(ud_ptr.cast::<()>()) + }; + + if builder.wrapped_getter.set(Box::new(getter)).is_err() { + panic!("Somehow the wrapped getter has already been set"); + } + + Ok(()) + } + + fn name() -> String { + let name = format!("{}Ref", T::name()); + name + } +} diff --git a/src/userdata/mod.rs b/src/userdata/mod.rs index 1cccaca..9dec6f7 100755 --- a/src/userdata/mod.rs +++ b/src/userdata/mod.rs @@ -7,6 +7,14 @@ use mlua_sys as lua; pub mod unsafe_ud; pub use unsafe_ud::*; +pub mod proxy; +#[allow(unused_imports)] +use proxy::*; + +pub mod borrow; +#[allow(unused_imports)] +use borrow::*; + /// An enum representing all Lua MetaMethods /// https://gist.github.com/oatmealine/655c9e64599d0f0dd47687c1186de99f pub enum MetaMethod { diff --git a/src/userdata/proxy.rs b/src/userdata/proxy.rs new file mode 100755 index 0000000..7e9611f --- /dev/null +++ b/src/userdata/proxy.rs @@ -0,0 +1,34 @@ +use std::marker::PhantomData; + +use crate::{AsLua, State, Userdata, UserdataBuilder, Value}; + +pub struct UserdataProxy(PhantomData); + +impl UserdataProxy { + pub fn new() -> Self { + Self(PhantomData) + } +} + +impl<'a, T: Userdata> AsLua<'a> for UserdataProxy { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { + todo!() + } +} + +impl Userdata for UserdataProxy { + fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { + let mut other = UserdataBuilder::::new(); + T::build(state, &mut other)?; + + // only the functions need to be added since they're the only thing usable from a proxy + builder.functions = other.functions; + + Ok(()) + } + + fn name() -> String { + let name = format!("{}Proxy", T::name()); + name + } +} \ No newline at end of file diff --git a/src/util.rs b/src/util.rs index 0d92715..0be8fde 100755 --- a/src/util.rs +++ b/src/util.rs @@ -1,10 +1,10 @@ use std::ffi::CStr; -use crate::State; +use crate::{FromLuaStack, PushToLuaStack, StackGuard, State, Value}; use mlua_sys as lua; -pub unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<()> { +pub(crate) unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<()> { let s = state.state_ptr(); let lua_type = lua::lua_type(s, idx); @@ -24,4 +24,40 @@ pub unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<() Err(crate::Error::UnexpectedType(exp_s.to_string(), s.to_string())) } +} + +/// Iterates through all entries in the registry and prints the types of each entry. +#[allow(dead_code)] +pub(crate) fn print_registry(lua: &State) -> crate::Result<()> { + unsafe { + let _g = StackGuard::new(&lua); + let s = lua.state_ptr(); + + Value::String(String::from("Hello World")).push_to_lua_stack(&lua)?; + let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX); + lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, r); + + for i in 0..r { + let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, i as i64); + + match ty { + lua::LUA_TUSERDATA => { + lua::lua_getmetatable(s, -1); + lua::lua_pushliteral(s, "__name"); + lua::lua_rawget(s, -2); + + let v = Value::from_lua_stack(&lua)?; + println!("{}: {}", i, v.as_string().unwrap()); + }, + _ => { + let tyname = CStr::from_ptr(lua::lua_typename(s, ty)); + let tyname = tyname.to_str().unwrap(); + println!("{}: {}", i, tyname); + } + } + + lua::lua_pop(s, 1); + } + } + Ok(()) } \ No newline at end of file