Skip to main content

intuicio_derive/
lib.rs

1//! Procedural macros that expose Rust items to the Intuicio runtime.
2//!
3//! Every Intuicio function, native or scripted, has the same shape:
4//! `fn(&mut Context, &Registry)`. It pops its arguments off the context
5//! stack and pushes its results back. Writing that shim by hand for each
6//! native function is noisy and easy to get wrong, so these macros generate
7//! it from the ordinary Rust signature, along with the description the
8//! registry needs.
9//!
10//! | Macro | Applied to | Produces |
11//! |---|---|---|
12//! | [`macro@intuicio_function`] | a free `fn` | a module named after the `fn`, holding `define_function` |
13//! | [`macro@intuicio_methods`] | an inherent `impl` | `<method>__define_function` for each marked method |
14//! | [`macro@intuicio_method`] | a method inside such an `impl` | nothing, it only marks the method |
15//! | [`macro@IntuicioStruct`] | a `struct` | an `IntuicioStruct::define_struct` impl |
16//! | [`macro@IntuicioEnum`] | a `#[repr(u8)]` `enum` | an `IntuicioEnum::define_enum` impl |
17//!
18//! Nothing registers itself. Each macro only writes a `define_*` function
19//! that you call, so registration stays explicit and ordered:
20//!
21//! ```ignore
22//! #[intuicio_function(module_name = "lib")]
23//! fn add(a: i32, b: i32) -> i32 {
24//!     a + b
25//! }
26//!
27//! registry.add_function(add::define_function(&registry));
28//! ```
29//!
30//! Every `define_*` function looks the types in its signature up in the
31//! registry by name and panics when one is missing, so types have to be
32//! registered before the functions that mention them.
33//!
34//! Rust visibility carries over: `pub` stays public, `pub(crate)` and
35//! `pub(in ...)` become `Visibility::Module`, and a private item becomes
36//! `Visibility::Private`.
37//!
38//! # Transformers
39//!
40//! `transformer = "SomeTransformer"` routes every argument and result through
41//! a `ValueTransformer`. The transformer decides which box travels on the stack
42//! in place of `T`, `&T` and `&mut T`, for example a managed value instead of a
43//! bare one. `dependency = "arg"` names the argument that a returned reference
44//! borrows from, so the transformer can tie the result to an owner that is
45//! still alive.
46//!
47//! # Attribute syntax
48//!
49//! Values are always string literals, even for names: `name = "add"`, not
50//! `name = add`. Flags such as `debug` stand alone.
51//!
52//! Registered names are kept as strings and never have to be Rust identifiers,
53//! so `name = "+"` or `module_name = "core/ops"` are fine. Only the names the
54//! script side sees are affected. The Rust items keep their own names.
55mod enum_type;
56mod function;
57mod methods;
58mod struct_type;
59
60use proc_macro::TokenStream;
61
62/// Wraps a free function so scripts can call it.
63///
64/// Generates a module named after the function, holding the
65/// `fn(&mut Context, &Registry)` shim, `define_signature` and
66/// `define_function`. The original function is emitted unchanged beside it.
67///
68/// ```ignore
69/// #[intuicio_function(module_name = "lib")]
70/// fn add(a: i32, b: i32) -> i32 {
71///     a + b
72/// }
73///
74/// registry.add_function(add::define_function(&registry));
75/// ```
76///
77/// # Attributes
78///
79/// | Attribute | Effect |
80/// |---|---|
81/// | `name = "..."` | registered name, defaults to the Rust name |
82/// | `module_name = "..."` | module the function is registered under |
83/// | `type_path = "..."` | associates the function with that type |
84/// | `use_registry` | the argument named `registry` receives `&Registry` instead of a stack value |
85/// | `use_context` | the argument named `context` receives `&mut Context` instead of a stack value |
86/// | `transformer = "..."` | routes arguments and results through a `ValueTransformer` |
87/// | `dependency = "..."` | argument a returned reference borrows from |
88/// | `meta = "..."` | `Meta` source attached to the function |
89/// | `args_meta(a = "...")` | `Meta` source attached to argument `a` |
90/// | `debug` | prints the generated code during compilation |
91///
92/// # Panics
93///
94/// Expanding panics on a `self` argument, or on an argument whose pattern is
95/// not a plain identifier. The generated `define_signature` panics at run
96/// time when a type from the signature is missing from the registry.
97#[proc_macro_attribute]
98pub fn intuicio_function(attributes: TokenStream, input: TokenStream) -> TokenStream {
99    crate::function::intuicio_function(attributes, input)
100}
101
102/// Describes a `struct` to the registry as a native type.
103///
104/// Implements `IntuicioStruct::define_struct`, reporting each field with the
105/// offset the Rust compiler chose, so the runtime reads the real layout
106/// instead of a copy.
107///
108/// ```ignore
109/// #[derive(IntuicioStruct, Default)]
110/// #[intuicio(name = "Bytes", module_name = "bytes")]
111/// pub struct Bytes {
112///     #[intuicio(ignore)]
113///     buffer: Vec<u8>,
114/// }
115/// ```
116///
117/// # Attributes
118///
119/// On the struct, inside `#[intuicio(...)]`:
120///
121/// | Attribute | Effect |
122/// |---|---|
123/// | `name = "..."` | registered name, defaults to the full Rust type name |
124/// | `module_name = "..."` | module the type is registered under |
125/// | `uninitialized` | describe the type without a default constructor, so scripts can hold values but not make one |
126/// | `override_send = bool` | claim or deny `Send` regardless of the Rust type |
127/// | `override_sync = bool` | claim or deny `Sync` regardless of the Rust type |
128/// | `override_copy = bool` | claim or deny copy semantics regardless of the Rust type |
129/// | `meta = "..."` | `Meta` source attached to the type |
130/// | `debug` | prints the generated code during compilation |
131///
132/// On a field: `name = "..."`, `ignore` to leave it out of the description,
133/// and `meta = "..."`. An ignored field still exists in Rust, scripts just
134/// cannot see it.
135///
136/// The three `override_*` attributes expand to `unsafe` calls. Use them only
137/// when you know the claim holds, since the runtime trusts them.
138///
139/// # Panics
140///
141/// Expanding panics on a tuple struct, because fields need names. The
142/// generated `define_struct` panics when a field type is not in the registry.
143#[proc_macro_derive(IntuicioStruct, attributes(intuicio))]
144pub fn intuicio_struct(input: TokenStream) -> TokenStream {
145    crate::struct_type::intuicio_struct(input)
146}
147
148/// Describes a `#[repr(u8)]` `enum` to the registry as a native type.
149///
150/// Implements `IntuicioEnum::define_enum`. Each variant is described with
151/// its discriminant and its fields, again at compiler-chosen offsets.
152///
153/// ```ignore
154/// #[derive(IntuicioEnum)]
155/// #[repr(u8)]
156/// #[intuicio(name = "Shape")]
157/// enum Shape {
158///     Empty,
159///     Circle { radius: f32 },
160/// }
161/// ```
162///
163/// # Attributes
164///
165/// On the enum, inside `#[intuicio(...)]`: `name`, `module_name`,
166/// `override_send`, `override_sync`, `override_copy`, `meta` and `debug`,
167/// all as on [`macro@IntuicioStruct`].
168///
169/// On a variant: `name = "..."`, `ignore`, `meta = "..."`, and `default` to
170/// mark the variant a default value starts in. On a variant field:
171/// `name = "..."`, `ignore` and `meta = "..."`.
172///
173/// Discriminants are counted from `0` upwards in declaration order. An
174/// explicit `= N` literal resets the count from there. A variant marked
175/// `ignore` still takes up its discriminant, so the ones after it keep the
176/// values Rust gave them.
177///
178/// # Panics
179///
180/// Expanding panics without `#[repr(u8)]`, on a non-literal or non-integer
181/// discriminant, or on a named field without a name. The generated
182/// `define_enum` panics when a field type is not in the registry.
183#[proc_macro_derive(IntuicioEnum, attributes(intuicio))]
184pub fn intuicio_enum(input: TokenStream) -> TokenStream {
185    crate::enum_type::intuicio_enum(input)
186}
187
188/// Wraps the methods of an inherent `impl` so scripts can call them.
189///
190/// Only methods carrying [`macro@intuicio_method`] are exposed. The rest of the
191/// block is left alone. For a method `foo` it adds `foo__intuicio_function`,
192/// `foo__define_signature` and `foo__define_function` to the same type.
193///
194/// ```ignore
195/// #[intuicio_methods(module_name = "bytes")]
196/// impl Bytes {
197///     #[intuicio_method(use_registry)]
198///     pub fn new(registry: &Registry) -> Reference { /* ... */ }
199/// }
200///
201/// registry.add_function(Bytes::new__define_function(&registry));
202/// ```
203///
204/// A `self` receiver becomes the first parameter, named `this`. The type the
205/// `impl` is for is attached to every signature, so methods stay grouped
206/// under it in the registry.
207///
208/// # Attributes
209///
210/// `module_name = "..."` and `transformer = "..."`. The transformer applies
211/// to every method in the block unless a method names its own.
212///
213/// # Panics
214///
215/// Expanding panics on a trait `impl`. Only inherent ones are supported.
216#[proc_macro_attribute]
217pub fn intuicio_methods(attributes: TokenStream, input: TokenStream) -> TokenStream {
218    crate::methods::intuicio_methods(attributes, input)
219}
220
221/// Marks a method inside an [`macro@intuicio_methods`] block for exposure.
222///
223/// Expands to the method unchanged. It exists so that the surrounding
224/// attribute can read its arguments.
225///
226/// # Attributes
227///
228/// `name`, `use_registry`, `use_context`, `transformer`, `dependency`,
229/// `meta`, `args_meta(...)` and `debug`, all as on
230/// [`macro@intuicio_function`]. `dependency = "this"` is the usual way to tie
231/// a returned reference to the receiver it came from.
232#[proc_macro_attribute]
233pub fn intuicio_method(_: TokenStream, input: TokenStream) -> TokenStream {
234    input
235}