use std::{ffi::CStr, str::Utf8Error, sync::Arc}; use lua::{lua_typename, lua_type}; 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::*; /* struct RustFn { } */ /* struct RustFnUpvalue { } */ /// pub fn ptr_to_string(ptr: *const i8) -> std::result::Result { let c = unsafe { CStr::from_ptr(ptr) }; let s= c.to_str()?; Ok(s.to_string()) } fn main() -> Result<()> { let lua = State::new(); lua.expose_libraries(&[StdLibrary::Debug]); 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 tbl = lua.create_table()?; let vec2_add = lua.create_function(|lua: &State, (a, b): (Table, Table)| -> Result { let ax: i32 = a.get("x")?; let ay: i32 = a.get("y")?; let bx: i32 = b.get("x")?; let by: i32 = b.get("y")?; let rx = ax + bx; let ry = ay + by; let mt = lua.create_meta_table("Vec2")?; mt.set("x", rx)?; mt.set("y", ry)?; Ok(mt) })?; let mt = lua.create_meta_table("Vec2")?; mt.set("x", 50)?; mt.set("y", 50)?; mt.set_meta("__add", vec2_add)?; globals.set("pos1", mt)?; let mt = lua.create_meta_table("Vec2")?; mt.set("x", 25)?; mt.set("y", 25)?; globals.set("pos2", mt)?; let tbl = lua.create_table()?; tbl.set("x", 10)?; //let globals = lua.globals()?; globals.set("X", tbl)?; lua.execute(r#" --[[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("Pos1 is (" .. pos1.x .. ", " .. pos1.y .. ")") print("Pos2 is (" .. pos2.x .. ", " .. pos2.y .. ")") local add_pos = pos1 + pos2 print("Pos1 + pos2 is (" .. add_pos.x .. ", " .. add_pos.y .. ")") "#).unwrap(); let num = globals.get::<_, i32>("cool_num")?; assert_eq!(num, 50); println!("Got number as 50!"); let num = globals.get::<_, Function>("say_number")?; num.exec::<_, ()>(50)?; let num = globals.get::<_, Function>("multiply_print")?; num.exec::<_, ()>((10, 5))?; let num = globals.get::<_, Function>("multiply_ret")?; let num: i32 = num.exec::<_, i32>((10, 5))?; assert_eq!(num, 50); println!("Did math in lua and got 50!"); 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 t = lua::lua_gettop(s); let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX); let t = lua::lua_gettop(s); if r == lua::LUA_REFNIL { Err(Error::Nil) } else { Ok(LuaRef::from(r)) } } } impl PushToLuaStack 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("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!("{}", self); let msg_c = msg.as_ptr() as *const i8; lua::luaL_error(lua, msg_c); panic!("never gets here"); } } /// A result for use with lua functions type Result = core::result::Result; pub trait PushToLuaStack { unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()>; } pub trait FromLuaStack<'a>: Sized { unsafe fn from_lua_stack(state: &'a State) -> Result; } /* impl<'a> FromLuaStack<'a> for () { unsafe fn from_lua_stack(state: &'a State) -> Result { Ok(()) } } */ /// Implements PushToLuaStack for a number macro_rules! impl_push_to_lua_stack_number { ($ty: ident) => { impl PushToLuaStack for $ty { unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { state.ensure_stack(1)?; lua::lua_pushnumber(state.state_ptr(), *self as f64); Ok(()) } } }; } impl<'a> FromLuaStack<'a> for i32 { unsafe fn from_lua_stack(state: &'a State) -> Result { let s = state.state_ptr(); if lua::lua_isnumber(s, -1) == 1 { let v = lua::lua_tonumber(s, -1) as i32; lua::lua_pop(s, 1); Ok(v) } else { let lua_ty = lua_type(s, -1); let typec = CStr::from_ptr(lua_typename(s, lua_ty)); let type_str = typec.to_str() .expect("Type has invalid bytes!"); Err(Error::unexpected_type("Number", type_str)) } } } impl_push_to_lua_stack_number!(i8); impl_push_to_lua_stack_number!(i16); impl_push_to_lua_stack_number!(i32); impl_push_to_lua_stack_number!(i64); impl_push_to_lua_stack_number!(u8); impl_push_to_lua_stack_number!(u16); impl_push_to_lua_stack_number!(u32); impl_push_to_lua_stack_number!(u64); impl_push_to_lua_stack_number!(f32); impl_push_to_lua_stack_number!(f64); impl PushToLuaStack 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 PushToLuaStack 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(()) } }