labt_proc_macro/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use proc_macro::TokenStream;
use quote::quote;
use syn::{
    parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Paren, FnArg, Item,
    PatTuple, PatType, Token, TypeTuple,
};
extern crate proc_macro;

#[proc_macro_attribute]
pub fn labt_lua(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let mut function = match parse_macro_input!(item as Item) {
        Item::Fn(item) => item,
        _ => panic!("This attribute is only applicable to functions"),
    };

    let name = &function.sig.ident;

    let sig = &function.sig;

    let function_visibility = &function.vis;
    let block = &function.block;
    let function_return = &function.sig.output;

    // obtain the first argument
    if let Some(first) = sig.inputs.first() {
        match first {
            FnArg::Typed(arg) => arg,
            _ => {
                return syn::Error::new(
                    first.span(),
                    "Only functions are allowed, methods are not supported",
                )
                .to_compile_error()
                .into()
            }
        }
    } else {
        return syn::Error::new(
            name.span(),
            "Incorrect function signature, at least the Lua context is required! as the first argument",
        )
        .to_compile_error()
        .into();
    };

    if function.sig.inputs.len() < 2 {
        // less than two args specified, add an empty tuple since the user
        // doesnt require args from lua
        function.sig.inputs.push(FnArg::Typed(PatType {
            pat: Box::new(syn::Pat::Tuple(PatTuple {
                attrs: vec![],
                paren_token: Paren::default(),
                elems: Punctuated::new(),
            })),
            ty: Box::new(syn::Type::Tuple(TypeTuple {
                paren_token: Paren::default(),
                elems: Punctuated::new(),
            })),
            attrs: vec![],
            colon_token: Token![:](function.sig.inputs.span()),
        }));
    }
    let params = &function.sig.inputs;

    let output: TokenStream = quote! {
         #function_visibility fn #name(lua: &mlua::Lua, table: &mlua::Table) -> mlua::Result<()> {
            let function = lua.create_function(move |#params|  #function_return
                #block
            )?;

            table.set(stringify!(#name), function)?;
            Ok(())
        }
    }
    .into();

    output
}