mlua_extras_derive/
lib.rs1#[macro_use]
2extern crate quote;
3
4use proc_macro::TokenStream;
5use proc_macro2::TokenStream as TokenStream2;
6use proc_macro_error::{proc_macro_error, abort};
7use syn::spanned::Spanned;
8use venial::{parse_item, Fields, Item};
9
10#[proc_macro_error]
11#[proc_macro_derive(UserData)]
12pub fn derive_user_data(input: TokenStream) -> TokenStream {
13    let input = TokenStream2::from(input);
14    let name = match parse_item(input.clone()) {
15        Ok(Item::Struct(struct_type)) => {
16            struct_type.name.clone()
17        },
18        Ok(Item::Enum(enum_type)) => {
19            enum_type.name.clone()
20        },
21        Err(err) => abort!(err.span(), "{}", err),
22        _ => abort!(input.span(), "only `struct` and `enum` types are supported for TypedUserData")
23    };
24
25    quote!(
26        impl mlua_extras::mlua::UserData for #name {
27            fn add_fields<'lua, F: mlua_extras::mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
28                let mut wrapper = mlua_extras::typed::WrappedBuilder::new(fields);
29                <#name as mlua_extras::typed::TypedUserData>::add_fields(&mut wrapper);
30            }
31
32            fn add_methods<'lua, M: mlua_extras::mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
33                let mut wrapper = mlua_extras::typed::WrappedBuilder::new(methods);
34                <#name as mlua_extras::typed::TypedUserData>::add_methods(&mut wrapper);
35            }
36        }
37    ).into()
38}
39
40#[proc_macro_error]
41#[proc_macro_derive(Typed, attributes(typed))]
42pub fn derive_typed(input: TokenStream) -> TokenStream {
43    let input = TokenStream2::from(input);
44    match parse_item(input.clone()) {
45        Ok(Item::Struct(struct_type)) => {
46            let name = struct_type.name.clone();
47            let label = name.to_string();
48            quote!(
49                impl mlua_extras::typed::Typed for #name {
50                    fn ty() -> mlua_extras::typed::Type {
51                        mlua_extras::typed::Type::class(mlua_extras::typed::TypedClassBuilder::new::<#name>())
52                    }
53
54                    fn as_param() -> mlua_extras::typed::Param {
55                        mlua_extras::typed::Param {
56                            doc: None,
57                            name: None,
58                            ty: mlua_extras::typed::Type::named(#label),
59                        }
60                    }
61                }
62            )
63        },
64        Ok(Item::Enum(enum_type)) => {
65            let variants = enum_type.variants
66                .iter()
67                .map(|(variant, _punc)| {
68                    let name = format!("\"{}\"", variant.name);
69                    match &variant.fields {
70                        Fields::Unit => quote!{ mlua_extras::typed::Type::Single(#name.into()) },
71                        Fields::Tuple(tf) => {
72                            let tuple_values = tf.fields.iter().map(|(field, _)| {
73                                let ty = field.ty.clone();
74                                quote!{ <#ty as mlua_extras::typed::Typed>::ty() }
75                            }).collect::<Vec<_>>();
76
77                            if tuple_values.len() == 1 {
78                                let first = tuple_values.first().unwrap();
79                                quote!{ #first }
80                            } else {
81                                quote!{ mlua_extras::typed::Type::Tuple(Vec::from([
82                                        #(#tuple_values,)*
83                                ])) }
84                            }
85                        },
86                        Fields::Named(named) => {
87                            let tuple_values = named.fields.iter().map(|(field, _)| {
88                                let name = field.name.to_string();
89                                let ty = field.ty.clone();
90                                quote!{ (mlua_extras::typed::Index::from(#name), <#ty as mlua_extras::typed::Typed>::ty()) }
91                            }).collect::<Vec<_>>();
92                            quote!{ mlua_extras::typed::Type::Table(std::collections::BTreeMap::from([
93                                    #(#tuple_values,)*
94                            ])) }
95                        }
96                    }
97                    
98                })
99                .collect::<Vec<_>>();
100
101            let name = enum_type.name.clone();
103            quote!(
104                impl mlua_extras::typed::Typed for #name {
105                    fn ty() -> mlua_extras::typed::Type {
106                        mlua_extras::typed::Type::r#enum(
107                            [ #(#variants,)* ]
108                        )
109                    }
110                }
111            )
112        },
113        Err(err) => abort!(err.span(), "{}", err),
114        _ => abort!(input.span(), "only `struct` and `enum` types are supported for Typed")
115    }.into()
116}