rlua_builders_derive/
lib.rs1use proc_macro::TokenStream;
14use proc_macro2::TokenStream as TokenStream2;
15use quote::quote;
16use syn::{
17 parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, FieldsNamed,
18 FieldsUnnamed, Ident, Index, Meta, MetaNameValue,
19};
20
21const DEFAULT: &'static str = "default";
22
23fn create_type_and_unwrap<'s>(
24 fields: impl Iterator<Item = &'s Field>,
25) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
26 fields
27 .map(|f| {
28 let ty = &f.ty;
29 const SYNTAX_ERR: &'static str = "Invalid syntax for default";
30 let def = f.attrs.iter().find(|a| a.path.is_ident(DEFAULT)).map(|a| {
31 match a.parse_meta().expect(SYNTAX_ERR) {
32 Meta::NameValue(MetaNameValue { lit, .. }) => quote!( .unwrap_or(#lit) ),
33 _ => panic!(SYNTAX_ERR),
34 }
35 });
36 match def {
37 Some(ts) => (quote! { ::std::option::Option<#ty> }, ts),
38 None => (quote! (#ty), quote!()),
39 }
40 })
41 .unzip()
42}
43
44fn builder_for_unnamed(name: TokenStream2, fields: &FieldsUnnamed) -> TokenStream2 {
45 let i = (0..fields.unnamed.len()).map(Index::from);
46 let (types, unwraps): (Vec<_>, Vec<_>) = create_type_and_unwrap(fields.unnamed.iter());
47 quote! {
48 ctx.create_function(|_, args: (#(#types,)*)| {
49 Ok(#name (#(args.#i #unwraps ,)*))
50 })
51 }
52}
53
54fn builder_for_named(name: TokenStream2, fields: &FieldsNamed) -> TokenStream2 {
55 let names = fields.named.iter().map(|x| &x.ident);
56 let (types, unwraps): (Vec<_>, Vec<_>) = create_type_and_unwrap(fields.named.iter());
57
58 quote! {
59 ctx.create_function(|_, data: rlua::Table<'s>| {
60 Ok(#name {
61 #( #names: data.get::<_, #types>(stringify!(#names))? #unwraps , )*
62 })
63 })
64 }
65}
66
67fn builder_for_fields(name: TokenStream2, fields: &Fields) -> TokenStream2 {
68 match fields {
69 Fields::Unit => quote! { Ok(#name) },
70 Fields::Unnamed(unnamed) => builder_for_unnamed(name, unnamed),
71 Fields::Named(named) => builder_for_named(name, named),
72 }
73}
74
75fn function_struct_builder(name: Ident, builder: TokenStream2) -> TokenStream2 {
76 quote! {
77 impl<'s> ::rlua_builders::LuaBuilder<'s, rlua::Function<'s>> for #name {
78 fn builder(ctx: rlua::Context<'s>) -> rlua::Result<rlua::Function<'s>> {
79 #builder
80 }
81 }
82 }
83}
84
85fn self_struct_builder(name: Ident, builder: TokenStream2) -> TokenStream2 {
86 quote! {
87 impl<'s> ::rlua_builders::LuaBuilder<'s, Self> for #name {
88 fn builder(ctx: rlua::Context<'s>) -> rlua::Result<Self> {
89 #builder
90 }
91 }
92 }
93}
94
95fn struct_builder(name: Ident, ds: DataStruct) -> TokenStream2 {
96 let code = builder_for_fields(quote! {Self}, &ds.fields);
97
98 match ds.fields {
99 Fields::Unit => self_struct_builder(name, code),
100 Fields::Unnamed(..) | Fields::Named(..) => function_struct_builder(name, code),
101 }
102}
103
104fn enum_builder(name: Ident, de: DataEnum) -> TokenStream2 {
105 let (names, builders): (Vec<_>, Vec<_>) = de
106 .variants
107 .iter()
108 .map(|v| {
109 let var_name = &v.ident;
110 (
111 var_name,
112 builder_for_fields(quote! {#name::#var_name}, &v.fields),
113 )
114 })
115 .unzip();
116
117 quote! {
118 impl<'s> ::rlua_builders::LuaBuilder<'s, rlua::Table<'s>> for #name {
119 fn builder(ctx: rlua::Context<'s>) -> rlua::Result<rlua::Table<'s>> {
120 let t = ctx.create_table()?;
121 #( t.set(stringify!(#names), #builders?)?; )*
122 Ok(t)
123 }
124 }
125 }
126}
127
128#[proc_macro_derive(LuaBuilder, attributes(default))]
135pub fn derive_struct_builder(input: TokenStream) -> TokenStream {
136 let input = parse_macro_input!(input as DeriveInput);
137 let name = input.ident;
138
139 let code = match input.data {
140 Data::Struct(ds) => struct_builder(name, ds),
141 Data::Enum(de) => enum_builder(name, de),
142 _ => panic!("Must annotate struct or enum"),
143 };
144
145 TokenStream::from(code)
146}
147
148#[proc_macro_derive(UserData)]
158pub fn derive_user_data(input: TokenStream) -> TokenStream {
159 let input = parse_macro_input!(input as DeriveInput);
160 let name = input.ident;
161
162 let expanded = quote! {
163 impl ::rlua::UserData for #name {}
164 };
165
166 TokenStream::from(expanded)
167}