mlua_gen/
lib.rs

1mod attr;
2mod r#enum;
3mod r#struct;
4
5use {
6    attr::Attributes,
7    proc_macro::TokenStream,
8    proc_macro2::TokenStream as TokenStream2,
9    quote::quote,
10    r#enum::enum_builder,
11    r#struct::struct_builder,
12    syn::{parse_macro_input, Data, DeriveInput},
13};
14
15pub(crate) use crate::attr::MethodOrFunction;
16
17macro_rules! dbg {
18    ($val:expr $(,)?) => {
19        // Use of `match` here is intentional because it affects the lifetimes
20        // of temporaries - https://stackoverflow.com/a/48732525/1063961
21        match $val {
22            tmp => {
23                #[cfg(feature = "debug")]
24                eprintln!(
25                    "[{}:{}:{}] {} = {}",
26                    std::file!(),
27                    std::line!(),
28                    std::column!(),
29                    std::stringify!($val),
30                    &tmp
31                );
32                tmp
33            },
34        }
35    };
36}
37
38/// # mlua_gen
39/// Allow quick modifications of Rust's struct and enum in Lua with `mlua`
40///
41/// ## Struct
42/// ### Example
43/// The following rust code
44/// ```rust
45/// use mlua_gen::mlua_gen;
46///
47/// #[mlua_gen(impl = [default(), age(&self), set_age(&mut self, u8)])]
48/// #[derive(Default)]
49/// pub(crate) struct Human {
50///     pub(crate) name: String,
51///     age: u8,
52/// }
53///
54/// impl Human {
55///     pub(crate) fn age(&self) -> String {
56///         format!("{} years old", self.age)
57///     }
58///     pub(crate) fn set_age(&mut self, age: u8) {
59///         self.age = age;
60///     }
61/// }
62/// ```
63///
64/// Can be used in lua easily like this
65/// ```lua
66/// local human = Human.default()
67/// human:set_age(42)
68/// human.name = "Martin"
69/// assert(human.name == "Martin")
70/// assert(human:age() == "42 years old")
71/// ```
72///
73/// ### Attributes
74/// #### `get`
75/// Value: `pub` | `pub(crate)` | `pub(super)` | `*` | `[Ident]`
76///
77/// Define which attributes should have getters values. (By default `pub(super)`)
78///
79/// - `pub(super)` includes `pub` and `pub(crate)`
80/// - `pub(crate)` includes `pub`
81/// - `*` represents all fields
82/// - `[Ident]` which fields should have getter ?
83///
84/// ##### Example
85/// ```rust,ignore
86/// #[mlua_gen(get = [a, c])]
87/// struct MyFields {
88///     a: usize,
89///     pub(super) b: usize,
90///     pub(crate) c: usize,
91///     pub d: usize,
92/// }
93/// ```
94/// Lua:
95/// ```lua
96/// print(fields.a)
97/// print(fields.b) -- nil
98/// print(fields.c)
99/// print(fields.d) -- nil
100/// ```
101///
102/// #### `set`
103/// Value: `pub` | `pub(crate)` | `pub(super)` | `*` | `[Ident]`
104///
105/// See `get` for more information about visibility
106///
107/// Define which attributes should have setters values. (By default `pub(super)`)
108///
109/// ##### Example
110/// ```rust,ignore
111/// #[mlua_gen(set = [a, c])]
112/// struct MyFields {
113///     a: usize,
114///     pub(super) b: usize,
115///     pub(crate) c: usize,
116///     pub d: usize,
117/// }
118/// ```
119/// Lua:
120/// ```lua
121/// fields.a = 42
122/// -- fields.b cannot be set
123/// fields.c = 42
124/// -- fields.d cannot be set
125/// ```
126///
127/// #### `impl`
128/// Value: `[ExprCall]` (`ExprCall` being a function call expression: `invoke(a, b)`)
129///
130/// The functions that are implemented for this struct and that you want to be usable in Lua
131///
132/// ##### Example
133/// ```rust,ignore
134/// #[mlua_gen(impl = [default(), age(&self), set_age(&mut self, u8)])]
135/// #[derive(Default)]
136/// pub(crate) struct Human {
137///     pub(crate) name: String,
138///     age: u8,
139/// }
140///
141/// impl Human {
142///     pub(crate) fn age(&self) -> String {
143///         format!("{} years old", self.age)
144///     }
145///     pub(crate) fn set_age(&mut self, age: u8) {
146///         self.age = age;
147///     }
148/// }
149/// ```
150///
151/// Lua:
152/// ```lua
153/// local human = Human.default()
154/// human:set_age(42)
155/// print(human:age()) -- 42
156/// ```
157///
158/// #### `custom_field`
159/// Value: `Ident`
160///
161/// The `Ident` should be the ident of a function that has for signature
162/// `fn(&mut ::mlua::UserDataFields<Self>) -> ();`
163///
164/// Posibility to add custom fields that will be usable in lua.
165///
166/// ##### Example
167/// ```rust,ignore
168/// #[mlua_gen(custom_fields = fields)]
169/// struct Human {
170///     pub name: String,
171///     pub age: u8,
172/// }
173/// fn fields<T: ::mlua::UserDataFields<Human>>(fields: &mut T) {
174///     fields.add_field_method_get("name_age", |_, this| {
175///         Ok(format!("{} ({})", this.name, this.age))
176///     });
177/// }
178/// ```
179/// Lua:
180/// ```lua
181/// human.age = 42
182/// human.name = "Martin"
183/// assert(human.name_age == "Martin (42)")
184/// ```
185///
186/// #### `custom_impls`
187/// Value: `Ident`
188///
189/// Same as `custom_field` but with `UserDataMethods`
190///
191/// ## Enum
192/// ### Example
193/// The following rust code
194/// ```rust
195/// use mlua_gen::mlua_gen;
196///
197/// #[mlua_gen(impl = [default(), name(&self)])]
198/// #[derive(Default)]
199/// enum Animal {
200///     #[default]
201///     Pig,
202///     Dog(String),
203///     Cat { name: String, age: u8 },
204/// }
205///
206/// impl Animal {
207///     pub(crate) fn name(&self) -> String {
208///         match self {
209///             Animal::Pig => "Piggy".to_owned(),
210///             Animal::Dog(name) => name.to_owned(),
211///             Animal::Cat { name, .. } => name.to_owned(),
212///         }
213///     }
214/// }
215/// ```
216///
217/// Can be used easily with the following lua code
218/// ```lua
219/// local pig = Animal.Pig -- or in our case, Animal:default()
220/// local dog = Animal.Dog ( "Doggo" )
221/// local cat = Animal.Cat { name = "Neko", age = 8 }
222///
223/// -- method call
224/// assert(pig:name() == "Piggy")
225/// assert(dog:name() == "Doggo")
226/// assert(cat:name() == "Neko")
227///
228/// -- access with field
229/// --- Pig
230/// assert(pig.pig)
231/// assert(pig.dog == nil)
232/// assert(pig.cat == nil)
233/// --- Dog
234/// assert(dog.pig == nil)
235/// assert(dog.dog[1] == "Doggo")
236/// assert(dog.cat == nil)
237/// --- Cat
238/// assert(cat.pig == nil)
239/// assert(cat.dog == nil)
240/// assert(cat.cat.name == "Neko")
241/// assert(cat.cat.age == 8)
242/// ```
243///
244#[proc_macro_attribute]
245pub fn mlua_gen(args: TokenStream, input: TokenStream) -> TokenStream {
246    let input = parse_macro_input!(input as DeriveInput);
247    let name = &input.ident;
248
249    let mut attributes = Attributes::default();
250    let attr_parser = syn::meta::parser(|meta| attributes.parse(meta));
251    parse_macro_input!(args with attr_parser);
252
253    let code = match input.data {
254        Data::Struct(ref ds) => {
255            match (|| -> syn::Result<TokenStream2> {
256                let field_get = attributes.get.fields_from_visibility(&ds.fields)?;
257                let field_set = attributes.set.fields_from_visibility(&ds.fields)?;
258                Ok(struct_builder(
259                    name,
260                    &ds.fields,
261                    field_get,
262                    field_set,
263                    attributes.custom_fields,
264                    attributes.r#impl,
265                    attributes.custom_impls,
266                ))
267            })() {
268                Ok(e) => dbg!(e),
269                Err(synerr) => return synerr.into_compile_error().into(),
270            }
271        },
272        Data::Enum(ref de) => {
273            dbg!(enum_builder(
274                name,
275                de.variants.iter(),
276                attributes.custom_fields,
277                attributes.custom_impls,
278            ))
279        },
280        _ => panic!("Must annotate struct"),
281    };
282
283    quote! {
284        #input
285
286        #code
287    }
288    .into()
289}