Skip to main content

wowlab_engine_macros/
lib.rs

1//! Procedural macros that generate the engine's layout and data-adapter boilerplate.
2
3use proc_macro::TokenStream;
4
5/// Define an opaque public error wrapper together with its private error-kind enum.
6///
7/// The input is a wrapper struct followed by its kind enum.
8/// The macro supplies the shared `Error` and wrapper-display scaffolding.
9/// Callers retain control of `Debug`, constructors, and classifications on the opaque wrapper.
10// WHY-PROC: validating and decorating paired struct/enum items requires item introspection.
11#[proc_macro]
12pub fn define_error(input: TokenStream) -> TokenStream {
13    wowlab_engine_macros_impl::define_error(input.into()).into()
14}
15
16/// Generate the forwarding macro directly from a handler trait's live method signatures.
17// WHY-PROC: the forwarder must inspect trait methods at their definition site so it cannot drift.
18#[proc_macro_attribute]
19pub fn spec_handler_delegation(attribute: TokenStream, item: TokenStream) -> TokenStream {
20    wowlab_engine_macros_impl::spec_handler_delegation(attribute.into(), item.into()).into()
21}
22
23/// Declare a non-generic, named-field dense-buffer slot.
24///
25/// # Example
26///
27/// ```
28/// pub mod _private {
29///     pub use bytemuck;
30/// }
31///
32/// wowlab_engine_macros::define_slot! {
33///     pub struct ExampleSlot {
34///         pub value: f64,
35///         pub count: i32,
36///         pub _padding: i32,
37///     }
38/// }
39///
40/// fn main() {
41///     let slot = ExampleSlot {
42///         value: 2.5,
43///         count: 3,
44///         _padding: 0,
45///     };
46///     let mut output = String::new();
47///     slot.dump_fields(&mut output);
48///     assert_eq!(ExampleSlot::OFF_VALUE, 0);
49///     assert!(output.contains("value"));
50///     assert!(!output.contains("_padding"));
51/// }
52/// ```
53// WHY-PROC: the slot DSL inspects fields and emits layout assertions and descriptor items.
54#[proc_macro]
55pub fn define_slot(input: TokenStream) -> TokenStream {
56    wowlab_engine_macros_impl::define_slot(input.into()).into()
57}
58
59/// Implement the configured DBC `HasId` trait for a named-field structure.
60///
61/// # Example
62///
63/// ```
64/// mod support {
65///     pub trait HasId {
66///         fn id(&self) -> i32;
67///     }
68/// }
69///
70/// use support::HasId as _;
71/// use wowlab_engine_macros::HasId;
72///
73/// #[derive(HasId)]
74/// #[id_field = "record_id"]
75/// #[has_id(crate = "crate::support")]
76/// struct Row {
77///     record_id: i32,
78/// }
79///
80/// fn main() {
81///     assert_eq!(Row { record_id: 42 }.id(), 42);
82/// }
83/// ```
84// WHY-PROC: deriving the ID accessor requires field introspection unavailable to macro_rules.
85#[proc_macro_derive(HasId, attributes(id_field, has_id))]
86pub fn derive_has_id(input: TokenStream) -> TokenStream {
87    wowlab_engine_macros_impl::derive_has_id(input.into()).into()
88}
89
90/// Implement the configured DBC `HasFk` trait for a named-field structure.
91///
92/// # Example
93///
94/// ```
95/// mod support {
96///     pub trait HasFk {
97///         fn fk(&self) -> i32;
98///     }
99/// }
100///
101/// use support::HasFk as _;
102/// use wowlab_engine_macros::HasFk;
103///
104/// #[derive(HasFk)]
105/// #[fk_field = "parent_id"]
106/// #[has_fk(crate = "crate::support")]
107/// struct Row {
108///     parent_id: i32,
109/// }
110///
111/// fn main() {
112///     assert_eq!(Row { parent_id: 7 }.fk(), 7);
113/// }
114/// ```
115// WHY-PROC: deriving the foreign-key accessor requires field introspection unavailable to macro_rules.
116#[proc_macro_derive(HasFk, attributes(fk_field, has_fk))]
117pub fn derive_has_fk(input: TokenStream) -> TokenStream {
118    wowlab_engine_macros_impl::derive_has_fk(input.into()).into()
119}
120
121/// Generate builder-to-runtime data lowering for a named-field structure.
122///
123/// # Example
124///
125/// ```
126/// use wowlab_engine_macros::LowerData;
127///
128/// struct RuntimeData {
129///     value: i32,
130/// }
131/// struct LowerContext;
132///
133/// #[derive(LowerData)]
134/// #[lower(data = "RuntimeData", ctx = "LowerContext")]
135/// struct Draft {
136///     #[lower(copy)]
137///     value: i32,
138/// }
139///
140/// let data = Draft { value: 11 }.into_data(&mut LowerContext);
141/// assert_eq!(data.value, 11);
142/// ```
143// WHY-PROC: lowering derives inspect field attributes and emit type-specific conversion impls.
144#[proc_macro_derive(LowerData, attributes(lower))]
145pub fn derive_lower_data(input: TokenStream) -> TokenStream {
146    wowlab_engine_macros_impl::derive_lower_data(input.into()).into()
147}
148
149/// Implement a `PostgreSQL` `COPY` row-binding contract for a named-field structure.
150///
151/// # Example
152///
153/// ```
154/// mod support {
155///     #[derive(Default)]
156///     pub struct CopyRow(pub Vec<String>);
157///
158///     impl CopyRow {
159///         pub fn push_bind(&mut self, value: impl ToString) -> &mut Self {
160///             self.0.push(value.to_string());
161///             self
162///         }
163///     }
164///
165///     pub trait CopyInsert {
166///         const COLUMNS: &'static [&'static str];
167///         fn copy_row(&self, row: &mut CopyRow, patch: &str);
168///     }
169///
170///     pub fn to_json<T>(_value: &T) -> &'static str {
171///         "null"
172///     }
173/// }
174///
175/// use support::CopyInsert as _;
176/// use wowlab_engine_macros::CopyInsert;
177///
178/// #[derive(CopyInsert)]
179/// #[copy_insert(crate = "crate::support")]
180/// struct Row {
181///     id: i32,
182///     name: String,
183/// }
184///
185/// fn main() {
186///     let mut output = support::CopyRow::default();
187///     Row { id: 7, name: "Ada".into() }.copy_row(&mut output, "v1");
188///     assert_eq!(output.0, ["7", "v1", "Ada"]);
189/// }
190/// ```
191// WHY-PROC: COPY-row derives inspect field attributes to generate columns and binding calls.
192#[proc_macro_derive(CopyInsert, attributes(copy_insert, copy))]
193pub fn derive_copy_insert(input: TokenStream) -> TokenStream {
194    wowlab_engine_macros_impl::derive_copy_insert(input.into()).into()
195}
196
197/// Generate a field-driven `Default` implementation for a resolved-data store.
198///
199/// Unannotated fields use `Default::default()`.
200/// `#[resolved(neutral = ...)]` constructs the field type with `new(neutral)`.
201/// `#[resolved(default = ...)]` uses the supplied expression directly.
202// WHY-PROC: store field types and initializer attributes require derive-time introspection.
203#[proc_macro_derive(ResolvedStore, attributes(resolved))]
204pub fn derive_resolved_store(input: TokenStream) -> TokenStream {
205    wowlab_engine_macros_impl::derive_resolved_store(input.into()).into()
206}