use std::{marker::PhantomData, 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::*; fn main() -> Result<()> { let lua = State::new(); lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); let globals = lua.globals()?; let a = |_lua: &State, (num,): (i32,)| -> Result { println!("Rust got number from lua: {}", num); Ok(999) }; let f = lua.create_function(a)?; globals.set("native_test", f)?; //let ud = lua.create_userdata("Vec2", Vec2 { x: 0.0, y: 0.0})?; let ud = lua.create_userdata("Vec2", UserdataProxy::::new())?; globals.set("Vec2", ud)?; let tbl = lua.create_table()?; tbl.set("x", 10)?; //let globals = lua.globals()?; globals.set("X", tbl)?; lua.execute(r#" require "util" --[[function dump_table(tbl) for k, v in pairs(tbl) do if type(v) == "table" then dump_table(v) elseif type(v) == "function" then else print(k .. "=" .. tostring(v)) end end end for k, v in pairs(_G) do --print("Found global named " .. k) if k == "X" then --dump_table(v) end end]]-- function multiply_print(a, b) print(a .. " * " .. b .. " = " .. a*b) end function multiply_ret(a, b) return a * b end function say_number(a) print("Lua says " .. a) end cool_num = 50 local res = native_test(50) print("Lua got " .. res .. " back from rust!") print("Vec2: " .. dump_table(Vec2)) print("Meta Vec2: " .. dump_table(getmetatable(Vec2))) --print("Vec2 is (" .. Vec2.x .. ", " .. Vec2.y .. ")") local v1 = Vec2.new(50, 50) print("v1 is (" .. v1.x .. ", " .. v1.y .. ")") local v2 = Vec2.new(500, 500) print("v2 is (" .. v2.x .. ", " .. v2.y .. ")") local v_add = v1 + v2 v_add.x = 90 print("v_add is (" .. v_add.x .. ", " .. v_add.y .. ")") "#).unwrap(); unsafe { assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack } Ok(()) } #[derive(Clone)] pub struct LuaRef(Arc); impl From for LuaRef { fn from(value: i32) -> Self { Self(Arc::new(value)) } } impl LuaRef { /// Creates a reference to what is at the top of the stack. pub unsafe fn from_stack(state: &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::from(r)) } } } impl<'a> PushToLuaStack<'a> for LuaRef { unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { let s = state.state_ptr(); unsafe { state.ensure_stack(1)?; let top = lua::lua_gettop(s); let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.0 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(()) } } #[derive(Debug, thiserror::Error)] pub enum Error { /// An error returned from lua #[error("Lua runtime error: {0}")] Runtime(String), /// Ran into a not enough memory error when trying to grow the lua stack. #[error("Ran out of memory when attempting to use `lua_checkstack`")] Oom, #[error("Ran into a nill value on the stack")] Nil, #[error("Unexpected type, expected {0} but got {1}")] UnexpectedType(String, String), #[error("Bad argument provided to {func:?}! Argument #{arg_index} (name: {arg_name:?}), cause: {error}")] BadArgument { func: Option, arg_index: i32, arg_name: Option, /// the error that describes what was wrong for this argument error: Arc }, #[error("Incorrect number of arguments, expected {arg_expected}, got {arg_count}")] IncorrectArgCount { arg_expected: i32, arg_count: i32, }, #[error("There is already a registry entry with the key {0}")] RegistryConflict(String) } impl Error { pub fn runtime(msg: &str) -> Self { Self::Runtime(msg.to_string()) } pub fn unexpected_type(expected: &str, got: &str) -> Self { Self::UnexpectedType(expected.to_string(), got.to_string()) } /// Throw the error in lua. /// /// This method never returns pub unsafe fn throw_lua(self, lua: *mut lua::lua_State) -> ! { let msg = format!("{}\0", self); let msg_c = msg.as_ptr() as *const i8; lua::luaL_error(lua, msg_c); unreachable!(); } } /// A result for use with lua functions type Result = core::result::Result; 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(()) } } /// 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(()) } } pub struct Vec2 { x: f32, y: f32, } impl Userdata for Vec2 { fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { builder.name("Vec2") .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", Vec2 { x, y, }) }) // method test .method("add", |lua, lhs: &Vec2, (rhs,): (&Vec2,)| { let lx = lhs.x; let ly = lhs.y; let rx = rhs.x; let ry = rhs.y; lua.create_userdata("Vec2", Vec2 { x: lx + rx, y: ly + ry, }) }) .meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (&Vec2,)| { let lx = lhs.x; let ly = lhs.y; let rx = rhs.x; let ry = rhs.y; lua.create_userdata("Vec2", Vec2 { x: lx + rx, y: ly + ry, }) }); Ok(()) } } 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>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { let mut other = UserdataBuilder::::new(); T::build(&mut other)?; let name = format!("{}Proxy", other.name.unwrap()); builder.name = Some(name); // only the functions need to be added since they're the only thing usable from a proxy builder.functions = other.functions; Ok(()) } }