Skip to main content

mlua_extras_derive/
lib.rs

1#[macro_use]
2extern crate quote;
3
4use proc_macro::TokenStream;
5use proc_macro_error::proc_macro_error;
6
7use crate::builder::Builder;
8
9mod methods;
10mod userdata;
11pub(crate) mod extract;
12pub(crate) mod builder;
13
14/// Generates a [mlua::UserData] implementation from struct fields.
15/// 
16/// Each named and unnamed field is automatically exposed to Lua as a read and/or write property or index.
17/// 
18/// Use `#[field(...)]` attributes to controll access and naming:
19/// 
20/// - `readonly`: Set the field to only be readable within Lua
21/// - `writeonly`: Set the field to only be writable within Lua
22/// - `skip`: Ignore generating and exposing the field
23/// - `rename`: Rename the field to a string for a named field and a digit for an indexed field
24/// 
25/// > Note: `readonly` + `writeonly` together is the same as having neither, the field will be exposed
26/// > for both read and write.
27/// 
28/// Optionally combine with [`macro@user_data_impl`] to also register methods in a rust like manner.
29/// 
30/// # Example
31/// 
32/// ```ignore
33/// #[derive(Clone, UserData)]
34/// struct Player {
35///     name: String,
36///     health: f64,
37///     #[field(skip)]
38///     handle: u64,
39///     #[field(readonly)]
40///     score: i32,
41///     #[field(rename = "pos_x")]
42///     position_x: f64,
43/// }
44/// ```
45/// 
46/// ```ignore
47/// #[derive(Clone, UserData)]
48/// enum PlayerAction {
49///     Idle,
50///     Move {
51///         x: i32,
52///         y: i32
53///     },
54///     Attack(
55///         #[field(rename = "name")]
56///         String
57///     ),
58///     Quit,
59/// }
60/// ```
61#[proc_macro_error]
62#[proc_macro_derive(UserData, attributes(field))]
63pub fn derive_user_data(input: TokenStream) -> TokenStream {
64    let input = syn::parse_macro_input!(input as syn::DeriveInput);
65    Builder::regular().derive_fields(input).into()
66}
67
68/// Attribute macro that registers methods from an `impl` block for use in Lua.
69/// 
70/// Used on an `impl` block for a type that derives [`UserData`](macro@UserData), this
71/// macro will register methods annotated with `#[method]`, `#[metamethod(...)]`, `#[getter(...)]`,
72/// `#[setter(...)]`, and `#[field]` along with const expressions with or without `#[field]`.
73/// 
74/// # Attributes
75/// 
76/// - `#[method]`
77///   - `#[method(rename = "name")`: register as a method with the provided name
78/// - `#[metamethod(...)]`
79///   - `#[metamethod(ToString)]`: register as a metamethod as a [`mlua::MetaMethod`] variant
80///   - `#[metamethod("__custom")]`: register as a custom named metamethod
81/// - `#[getter(...)]`
82///   - `#[getter("field")]`: register the function as a getter for the named field
83/// - `#[setter(...)]`
84///   - `#[setter("field")]`: register the function as a setter for the named field
85/// - `#[field(...)]`
86///   - Applied to a function will call the function once to register a static field
87///   - Applied to a `const` expr will register the value as a static field
88///   - `#[field(rename="field")]`: register field with the custom name
89///   - `#[field(skip)]`: ignore the function or `const` expr and don't register it
90/// 
91/// # Patterns
92/// 
93/// - `&self`: registered with `mlua::UserDataMethods::add_method` or `mlua::UserDataMethods::add_meta_method`
94/// - `&mut self`: registered with `mlua::UserDataMethods::add_method_mut` or `mlua::UserDataMethods::add_meta_method_mut`
95/// - without `self`: registered with `mlua::UserDataMethods::add_function` or `mlua::UserDataMethods::add_meta_function`
96/// - `async fn`: registered with the `mlua::UserDataMethods::add_async_*` variant that matches the above arguments
97/// - If the first non `self` parameter is `lua` then `&mlua::Lua` is passed to non async methods/functions
98///     and `mlua::Lua` is passed into async methods/functions
99/// 
100/// # Return
101/// 
102/// - `Result<T, E>` where `E: Into<mlua::Error>`: Method is fallible and the error is automatically converted to a [`mlua::Error`].
103///     This includes any error type that implements [`mlua::ExternalError`] and any return type that has the name `Result`.
104/// - `T`: Method is infallible and is wrapped with `Ok(...)` when registered
105/// - `()`: Method is infallible and has no return value. Registration returns `Ok(())`
106/// 
107/// All methods stay as is and stay as regular callable rust functions. Any methods without one of the listed attribute macros will not be registered.
108/// 
109/// # Example
110/// 
111/// ```ignore
112/// #[derive(Clone, TypedUserData)]
113/// struct Counter { value: i64 }
114/// 
115/// #[typed_user_data_impl]
116/// impl Counter {
117///     const COUNT: usize = 10;
118/// 
119///     #[field]
120///     fn max() -> i64 {
121///         i64::MAX
122///     }
123/// 
124///     #[field(rename = "MIN")]
125///     fn min() -> i64 {
126///         0
127///     }
128/// 
129///     #[getter("direction")]
130///     fn get_direction(&self) -> String {
131///         "west".into()
132///     }
133/// 
134///     #[setter("direction")]
135///     fn set_direction(&mut self, dir: String) {
136///         _ = dir;
137///     }
138/// 
139///     #[method]
140///     fn get(&self) -> i64 { self.value }
141/// 
142///     #[method]
143///     fn increment(&mut self) { self.value += 1 }
144/// 
145///     #[method]
146///     fn create_table(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
147///         lua.create_table()
148///     }
149/// 
150///     #[metamethod(ToString)]
151///     fn to_string(&self) -> String { format!("Counter({})", self.value) }
152/// 
153///     // Requires the `async` feature
154///     // Must be accessed from lua code with an entry of `mlua::Chunk::eval_async` or `mlua::Chunk::exec_async`
155///     #[method]
156///     async fn fetch(&self, lua: mlua::Lua, url: String) -> mlua::Result<String> {
157///         _ = lua;
158///         Ok(format!("fetched: {url}"))
159///     }
160/// }
161/// ```
162#[proc_macro_error]
163#[proc_macro_attribute]
164pub fn user_data_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
165    let item = syn::parse_macro_input!(item as syn::ItemImpl);
166    Builder::regular().derive_methods(item).into()
167}
168
169/// Generates a [`Typed`](mlua_extras::Typed) implementation from fields.
170/// 
171/// Only supports structs and enums.
172/// 
173/// This registers the target as a new Lua type that can be used
174/// to generate documentation.
175/// 
176/// # Structs
177/// 
178/// Assigned as a lua `class` with it's registered fields, indexes, methods,
179/// functions and their meta variants.
180/// 
181/// # Enums
182/// 
183/// Assigned as an alias to a union of `class`es where each `class` is a enum variant. This
184/// is to best represent rust's use of enums as unions.
185#[proc_macro_error]
186#[proc_macro_derive(Typed)]
187pub fn derive_typed(input: TokenStream) -> TokenStream {
188    let item = syn::parse_macro_input!(input as syn::DeriveInput);
189    Builder::derive_typed(&item, false).into()
190}
191
192/// Generates a [mlua_extras::typed::TypedUserData] implementation from struct fields.
193/// 
194/// Each named and unnamed field is automatically exposed to Lua as a read and/or write property or index.
195/// 
196/// Use `#[field(...)]` attributes to controll access and naming:
197/// 
198/// - `readonly`: Set the field to only be readable within Lua
199/// - `writeonly`: Set the field to only be writable within Lua
200/// - `skip`: Ignore generating and exposing the field
201/// - `rename`: Rename the field to a string for a named field and a digit for an indexed field
202/// 
203/// > Note: `readonly` + `writeonly` together is the same as having neither, the field will be exposed
204/// > for both read and write.
205/// 
206/// Optionally combine with [`macro@typed_user_data_impl`] to also register methods in a rust like manner.
207/// 
208/// # Example
209/// 
210/// ```ignore
211/// #[derive(Clone, TypedUserData)]
212/// struct Player {
213///     name: String,
214///     health: f64,
215///     #[field(skip)]
216///     handle: u64,
217///     #[field(readonly)]
218///     score: i32,
219///     #[field(rename = "pos_x")]
220///     position_x: f64,
221/// }
222/// ```
223/// 
224/// ```ignore
225/// #[derive(Clone, TypedUserData)]
226/// enum PlayerAction {
227///     Idle,
228///     Move {
229///         x: i32,
230///         y: i32
231///     },
232///     Attack(
233///         #[field(rename = "name")]
234///         String
235///     ),
236///     Quit,
237/// }
238/// ```
239#[proc_macro_error]
240#[proc_macro_derive(TypedUserData, attributes(field))]
241pub fn derive_typed_user_data(input: TokenStream) -> TokenStream {
242    let input = syn::parse_macro_input!(input as syn::DeriveInput);
243    Builder::typed().derive_fields(input).into()
244}
245
246/// Attribute macro that registers methods from an `impl` block for use in Lua.
247/// 
248/// Used on an `impl` block for a type that derives [`TypedUserData`](macro@TypedUserData), this
249/// macro will register methods annotated with `#[method]`, `#[metamethod(...)]`, `#[getter(...)]`,
250/// `#[setter(...)]`, and `#[field]` along with const expressions with or without `#[field]`.
251/// 
252/// # Attributes
253/// 
254/// - `#[method]`
255///   - `#[method(rename = "name")`: register as a method with the provided name
256/// - `#[metamethod(...)]`
257///   - `#[metamethod(ToString)]`: register as a metamethod as a [`mlua::MetaMethod`] variant
258///   - `#[metamethod("__custom")]`: register as a custom named metamethod
259/// - `#[getter(...)]`
260///   - `#[getter("field")]`: register the function as a getter for the named field
261/// - `#[setter(...)]`
262///   - `#[setter("field")]`: register the function as a setter for the named field
263/// - `#[field(...)]`
264///   - Applied to a function will call the function once to register a static field
265///   - Applied to a `const` expr will register the value as a static field
266///   - `#[field(rename="field")]`: register field with the custom name
267///   - `#[field(skip)]`: ignore the function or `const` expr and don't register it
268/// 
269/// # Patterns
270/// 
271/// - `&self`: registered with `TypedDataMethods::add_method` or `TypedDataMethods::add_meta_method`
272/// - `&mut self`: registered with `TypedDataMethods::add_method_mut` or `TypedDataMethods::add_meta_method_mut`
273/// - without `self`: registered with `TypedDataMethods::add_function` or `TypedDataMethods::add_meta_function`
274/// - `async fn`: registered with the `TypedDataMethods::add_async_*` variant that matches the above arguments
275/// - If the first non `self` parameter is `lua` then `&mlua::Lua` is passed to non async methods/functions
276///     and `mlua::Lua` is passed into async methods/functions
277/// 
278/// # Return
279/// 
280/// - `Result<T, E>` where `E: Into<mlua::Error>`: Method is fallible and the error is automatically converted to a [`mlua::Error`].
281///     This includes any error type that implements [`mlua::ExternalError`] and any return type that has the name `Result`.
282/// - `T`: Method is infallible and is wrapped with `Ok(...)` when registered
283/// - `()`: Method is infallible and has no return value. Registration returns `Ok(())`
284/// 
285/// All methods stay as is and stay as regular callable rust functions. Any methods without one of the listed attribute macros will not be registered.
286/// 
287/// # Example
288/// 
289/// ```ignore
290/// #[derive(Clone, TypedUserData)]
291/// struct Counter { value: i64 }
292/// 
293/// #[typed_user_data_impl]
294/// impl Counter {
295///     const COUNT: usize = 10;
296/// 
297///     #[field]
298///     fn max() -> i64 {
299///         i64::MAX
300///     }
301/// 
302///     #[field(rename = "MIN")]
303///     fn min() -> i64 {
304///         0
305///     }
306/// 
307///     #[getter("direction")]
308///     fn get_direction(&self) -> String {
309///         "west".into()
310///     }
311/// 
312///     #[setter("direction")]
313///     fn set_direction(&mut self, dir: String) {
314///         _ = dir;
315///     }
316/// 
317///     #[method]
318///     fn get(&self) -> i64 { self.value }
319/// 
320///     #[method]
321///     fn increment(&mut self) { self.value += 1 }
322/// 
323///     #[method]
324///     fn create_table(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
325///         lua.create_table()
326///     }
327/// 
328///     #[metamethod(ToString)]
329///     fn to_string(&self) -> String { format!("Counter({})", self.value) }
330/// 
331///     // Requires the `async` feature
332///     // Must be accessed from lua code with an entry of `mlua::Chunk::eval_async` or `mlua::Chunk::exec_async`
333///     #[method]
334///     async fn fetch(&self, lua: mlua::Lua, url: String) -> mlua::Result<String> {
335///         _ = lua;
336///         Ok(format!("fetched: {url}"))
337///     }
338/// }
339/// ```
340#[proc_macro_error]
341#[proc_macro_attribute]
342pub fn typed_user_data_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
343    let item = syn::parse_macro_input!(item as syn::ItemImpl);
344    Builder::typed().derive_methods(item).into()
345}