1#[macro_use]
2extern crate syn;
3
4#[macro_use]
5extern crate quote;
6
7use proc_macro::TokenStream;
8use quote::ToTokens;
9use syn::ItemFn;
10
11macro_rules! wrap_compile_error {
12 ($input:ident, $code:expr) => {{
13 let orig_tokens = $input.clone();
14 match (|| -> Result<TokenStream, syn::Error> { $code })() {
15 Ok(tokens) => tokens,
16 Err(_) => return orig_tokens
17 }
18 }};
19}
20
21fn check_lua_function(input: &mut ItemFn) {
22 assert!(input.sig.asyncness.is_none(), "Cannot be async");
23 assert!(input.sig.constness.is_none(), "Cannot be const");
24 assert!(input.sig.inputs.len() == 1, "There can only be one argument, and it should be a pointer to the Lua state (gmod::lua::State)");
25 assert!(input.sig.abi.is_none() || input.sig.abi.as_ref().and_then(|abi| abi.name.as_ref()).map(|abi| abi.value() == "C-unwind").unwrap_or(true), "Do not specify an ABI");
26 input.sig.abi = Some(syn::parse_quote!(extern "C-unwind"));
27}
28
29fn genericify_return(item_fn: &mut ItemFn) {
30 let stmts = std::mem::take(&mut item_fn.block.stmts);
31 let output = std::mem::replace(&mut item_fn.sig.output, parse_quote!(-> i32));
32 item_fn.block.stmts = vec![syn::parse2(quote!({::gmod::lua::ValuesReturned::from((|| #output {#(#stmts);*})()).into()})).unwrap()];
33}
34
35#[proc_macro_attribute]
36pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
37 wrap_compile_error!(tokens, {
38 let mut input = syn::parse::<ItemFn>(tokens)?;
39
40 let lua_ident = format_ident!("{}", match &input.sig.inputs[0] {
41 syn::FnArg::Typed(arg) => arg.pat.to_token_stream().to_string(),
42 _ => unreachable!(),
43 });
44
45 input.block.stmts.insert(0, syn::parse2(quote!(::gmod::lua::__set_state__internal(#lua_ident);)).unwrap());
47
48 input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
50
51 check_lua_function(&mut input);
53
54 input.attrs.push(parse_quote!(#[no_mangle]));
56
57 genericify_return(&mut input);
59
60 Ok(input.into_token_stream().into())
61 })
62}
63
64#[proc_macro_attribute]
65pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
66 wrap_compile_error!(tokens, {
67 let mut input = syn::parse::<ItemFn>(tokens)?;
68
69 check_lua_function(&mut input);
71
72 input.attrs.push(parse_quote!(#[no_mangle]));
74
75 #[cfg(feature = "gmcl")] {
77 let stmts = std::mem::take(&mut input.block.stmts);
78 input.block.stmts = vec![syn::parse2(quote!({
79 let ret = (|| {#(#stmts);*})();
80 ::gmod::gmcl::restore_stdout();
81 ret
82 })).unwrap()];
83 }
84
85 genericify_return(&mut input);
87
88 Ok(input.into_token_stream().into())
89 })
90}
91
92#[proc_macro_attribute]
93pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
94 wrap_compile_error!(tokens, {
95 let mut input = syn::parse::<ItemFn>(tokens)?;
96
97 check_lua_function(&mut input);
99
100 genericify_return(&mut input);
102
103 Ok(input.into_token_stream().into())
104 })
105}