locenv_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, ItemFn};
4
5/// Specify that the function is a module loader.
6///
7/// See https://www.lua.org/manual/5.4/manual.html#6.3 for more information.
8///
9/// # Examples
10///
11/// ```no_run
12/// use locenv::api::LuaState;
13/// use locenv_macros::loader;
14/// use std::os::raw::c_int;
15///
16/// #[loader]
17/// extern "C" fn loader(lua: *mut LuaState) -> c_int {
18///     0
19/// }
20/// ```
21#[proc_macro_attribute]
22pub fn loader(_: TokenStream, item: TokenStream) -> TokenStream {
23    let input = parse_macro_input!(item as ItemFn);
24    let loader = &input.sig.ident;
25    let result = quote! {
26        #input
27
28        #[no_mangle]
29        pub unsafe extern "C" fn bootstrap(bootstrap: *const locenv::api::BootstrapContext, api: *const locenv::api::ApiTable) -> std::os::raw::c_int {
30            if locenv::API_TABLE.is_null() {
31                locenv::API_TABLE = api;
32            }
33
34            let lua = (*bootstrap).lua;
35            let context = locenv::Context::new(bootstrap);
36
37            locenv::push_fn(lua, #loader, 0);
38
39            // Move context to user data.
40            let raw = Box::into_raw(Box::new(context));
41            let ptr = std::mem::size_of::<*mut locenv::Context>();
42            let ud = ((*api).lua_newuserdatauv)(lua, ptr, 1);
43
44            ud.copy_from_nonoverlapping(std::mem::transmute(&raw), ptr);
45
46            // Associate the userdata with metatable.
47            if ((*api).aux_newmetatable)(lua, (*bootstrap).name) == 0 {
48                let context = Box::from_raw(raw);
49
50                locenv::pop(lua, 3); // Pop metatable + user data + loader.
51                locenv::push_str(lua, &format!("someone already created a metatable named '{}'", context.module_name()));
52
53                return 1;
54            }
55
56            ((*api).lua_pushstring)(lua, b"__gc\0".as_ptr() as *const _);
57            ((*api).lua_pushstring)(lua, (*bootstrap).name);
58            ((*api).lua_pushcclosure)(lua, locenv::Context::finalize, 1);
59            ((*api).lua_settable)(lua, -3);
60            ((*api).lua_setmetatable)(lua, -2);
61
62            2
63        }
64    };
65
66    result.into()
67}