use std::{sync::Arc, ffi::CStr}; 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::*; fn main() -> Result<()> { let lua = State::new(); lua.expose_libraries(&[StdLibrary::Debug]); 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]]-- print("x is " .. X.x) cool_num = 50 function say_number(num) print("I'm lua and I said " .. num) end function multiply_print(a, b) print(a .. " * " .. b .. " = " .. a * b) end function multiply_ret(a, b) return a * b end "#).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 r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX); 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)?; lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.0 as i64); } Ok(()) } } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Lua runtime error: {0}")] /// An error returned from lua Runtime(String), #[error("Ran out of memory when attempting to use `lua_checkstack`")] /// Ran into a not enough memory error when trying to grow the lua stack. Oom, #[error("Ran into a nill value on the stack")] Nil, #[error("Unexpected type, expected {0} but got {1}")] UnexpectedType(String, 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()) } } /// 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; } /// 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(()) } }