diff --git a/Cargo.toml b/Cargo.toml index 9769c8c..cb131f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,11 @@ license = "MIT" # [badges] # travis-ci = { repository = "khvzak/mlua", branch = "master" } +[workspace] +members = [ + "mlua_derive", +] + [dependencies] libc = { version = "0.2" } num-traits = { version = "0.2.6" } diff --git a/mlua_derive/Cargo.toml b/mlua_derive/Cargo.toml new file mode 100644 index 0000000..3a31364 --- /dev/null +++ b/mlua_derive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "mlua_derive" +version = "0.1.0" +authors = ["Alex Orlenko "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "1.0", features = ["full"] } diff --git a/mlua_derive/src/lib.rs b/mlua_derive/src/lib.rs new file mode 100644 index 0000000..e382c32 --- /dev/null +++ b/mlua_derive/src/lib.rs @@ -0,0 +1,35 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::quote_spanned; +use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn}; + +#[proc_macro_attribute] +pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attr as AttributeArgs); + let item = parse_macro_input!(item as ItemFn); + + if args.len() > 0 { + let err = Error::new(Span::call_site(), "the number of arguments must be zero") + .to_compile_error(); + return err.into(); + } + + let span = item.span(); + let item_name = item.sig.ident.clone(); + let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site()); + + let wrapped = quote_spanned! { span => + #[no_mangle] + unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int { + #item + + mlua::Lua::init_from_ptr(state) + .entrypoint1(#item_name) + .unwrap() + } + }; + + wrapped.into() +} diff --git a/src/lua.rs b/src/lua.rs index 46094d3..d0c1c8c 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -100,6 +100,18 @@ impl Lua { } } + // Executes module entrypoint function, which returns only one Value. + // The returned value then pushed to the Lua stack. + #[doc(hidden)] + pub fn entrypoint1<'lua, 'callback, R, F>(&'lua self, func: F) -> Result + where + R: ToLua<'callback>, + F: 'static + Send + Fn(&'callback Lua) -> Result, + { + let cb = self.create_callback(Box::new(move |lua, _| func(lua)?.to_lua_multi(lua)))?; + unsafe { self.push_value(cb.call(())?).map(|_| 1) } + } + /// Returns true if the garbage collector is currently running automatically. pub fn gc_is_running(&self) -> bool { unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCISRUNNING, 0) != 0 } @@ -718,8 +730,7 @@ impl Lua { } // Uses 2 stack spaces, does not call checkstack - // TODO: return to original - pub unsafe fn push_value(&self, value: Value) -> Result<()> { + pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> { match value { Value::Nil => { ffi::lua_pushnil(self.state); @@ -770,8 +781,7 @@ impl Lua { } // Uses 2 stack spaces, does not call checkstack - // TODO: return to original - pub unsafe fn pop_value(&self) -> Value { + pub(crate) unsafe fn pop_value(&self) -> Value { match ffi::lua_type(self.state, -1) { ffi::LUA_TNIL => { ffi::lua_pop(self.state, 1);