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}