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}