wasserxr_macros/lib.rs
1//! Procedural macros that generate WasserXR's C ABI plugin bindings.
2//!
3//! These macros let plugin authors write normal Rust systems, components, and
4//! asset types while emitting the `wxr_*` functions the runtime resolves from
5//! loaded plugins.
6
7mod asset;
8mod component;
9mod system;
10
11use proc_macro::TokenStream;
12use syn::{Error, ItemFn, ItemStruct, parse_macro_input};
13
14/// Turns a Rust system attach function into the C ABI function WasserXR needs.
15///
16/// Use it on a function with this shape:
17///
18/// ```ignore
19/// #[attacher(my_system)]
20/// fn attach_my_system(scene: &mut wasserxr::scene::Scene) {
21/// // ...
22/// }
23/// ```
24///
25/// The macro exports `wxr_attach_<system>`.
26#[proc_macro_attribute]
27pub fn attacher(args: TokenStream, item: TokenStream) -> TokenStream {
28 let args = parse_macro_input!(args as system::LifecycleArgs);
29 let item = parse_macro_input!(item as ItemFn);
30
31 system::expand_attacher(args, item)
32 .unwrap_or_else(Error::into_compile_error)
33 .into()
34}
35
36/// Turns a Rust system detach function into the C ABI function WasserXR needs.
37///
38/// Use it on a function with this shape:
39///
40/// ```ignore
41/// #[detacher(my_system)]
42/// fn detach_my_system(scene: &mut wasserxr::scene::Scene) {
43/// // ...
44/// }
45/// ```
46///
47/// The macro exports `wxr_detach_<system>`.
48#[proc_macro_attribute]
49pub fn detacher(args: TokenStream, item: TokenStream) -> TokenStream {
50 let args = parse_macro_input!(args as system::LifecycleArgs);
51 let item = parse_macro_input!(item as ItemFn);
52
53 system::expand_detacher(args, item)
54 .unwrap_or_else(Error::into_compile_error)
55 .into()
56}
57
58/// Turns a Rust system function into the C ABI functions WasserXR needs.
59///
60/// Use it on a function with this shape:
61///
62/// ```ignore
63/// #[system(entities = [["Transform", "Mesh"], ["Camera"]])]
64/// fn render(scene: &mut wasserxr::scene::Scene, entities: Vec<Vec<uuid::Uuid>>) {
65/// // ...
66/// }
67/// ```
68///
69/// The macro exports `WXR_GROUPS_<SYSTEM>`, `wxr_select_<system>`, and
70/// `wxr_system_<system>`. The first matching entity group wins.
71#[proc_macro_attribute]
72pub fn system(args: TokenStream, item: TokenStream) -> TokenStream {
73 let args = parse_macro_input!(args as system::Args);
74 let item = parse_macro_input!(item as ItemFn);
75
76 system::expand(args, item)
77 .unwrap_or_else(Error::into_compile_error)
78 .into()
79}
80
81/// Turns a Rust component struct into the C ABI functions WasserXR needs.
82///
83/// Use it on a named-field struct:
84///
85/// ```ignore
86/// #[component(no_schema)]
87/// #[virtual_field(x: f32, getter = custom_x_getter, mutable)]
88/// #[derive(Default)]
89/// struct MyComponent {
90/// value: i32,
91/// #[getter(custom_name_getter)]
92/// #[mutable]
93/// name: String,
94/// #[none]
95/// internal: i32,
96/// }
97/// ```
98///
99/// The macro exports destroy and schema functions for the component.
100/// Use `#[component(no_schema)]` to skip schema generation and provide a custom
101/// `wxr_schema_<Component>` function yourself.
102/// Fields without field function attributes get generated getter, serializer,
103/// and deserializer functions by default. Use `#[mutable]` to allow mutable
104/// references through `Scene::query_mut`. If at least one field function
105/// attribute is present, only the requested functions are generated. Field
106/// function attributes can also take a custom function path, for example
107/// `#[getter(my_getter)]`. Use `#[none]` to register a field without generated
108/// field functions. Generated serializers for complex fields use serde through
109/// bincode, so those field types must implement serde's serialize and
110/// deserialize traits. Use `#[virtual_field(name: Type, getter = my_getter)]`
111/// to register a queryable field that is not stored directly in the struct.
112#[proc_macro_attribute]
113pub fn component(args: TokenStream, item: TokenStream) -> TokenStream {
114 let args = parse_macro_input!(args as component::Args);
115 let item = parse_macro_input!(item as ItemStruct);
116
117 component::expand(args, item)
118 .unwrap_or_else(Error::into_compile_error)
119 .into()
120}
121
122/// Wraps `fn create(scene: &mut Scene) -> Option<Component>` as a component creator.
123///
124/// The macro exports `wxr_create_<Component>` and maps `None` to a null pointer.
125#[proc_macro_attribute]
126pub fn component_creator(args: TokenStream, item: TokenStream) -> TokenStream {
127 let args = parse_macro_input!(args as component::CreatorArgs);
128 let item = parse_macro_input!(item as ItemFn);
129
130 component::expand_component_creator(args, item)
131 .unwrap_or_else(Error::into_compile_error)
132 .into()
133}
134
135/// Turns a Rust asset struct into the C ABI functions WasserXR needs.
136///
137/// The macro exports destroy, schema, and getter functions for the asset type.
138/// Every named field is queryable unless it has `#[none]`.
139#[proc_macro_attribute]
140pub fn asset_type(args: TokenStream, item: TokenStream) -> TokenStream {
141 if !args.is_empty() {
142 return Error::new(
143 proc_macro2::Span::call_site(),
144 "`asset_type` does not support arguments",
145 )
146 .into_compile_error()
147 .into();
148 }
149
150 let item = parse_macro_input!(item as ItemStruct);
151
152 asset::expand_asset_type(item)
153 .unwrap_or_else(Error::into_compile_error)
154 .into()
155}
156
157/// Wraps `fn create(scene: &mut Scene, data: &str) -> Option<AssetType>` as an asset creator.
158///
159/// The macro exports `wxr_asset_create_<AssetType>` and maps `None` or invalid
160/// C strings to a null pointer.
161#[proc_macro_attribute]
162pub fn asset_type_creator(args: TokenStream, item: TokenStream) -> TokenStream {
163 let args = parse_macro_input!(args as asset::CreatorArgs);
164 let item = parse_macro_input!(item as ItemFn);
165
166 asset::expand_asset_type_creator(args, item)
167 .unwrap_or_else(Error::into_compile_error)
168 .into()
169}