Skip to main content

icydb_model_macros/
lib.rs

1//! Module: lib
2//! Responsibility: application-model declaration and helper macros.
3//! Does not own: runtime schema semantics.
4//! Boundary: macro input to generated tokens.
5
6extern crate syn_darling as syn;
7
8mod authoring_types;
9mod case;
10mod crate_path;
11mod r#gen;
12mod helper;
13mod helper_deref;
14mod helper_display;
15mod helper_inner;
16mod helper_newtype;
17mod helper_ops;
18mod imp;
19mod node;
20mod predicate;
21mod trait_kind;
22mod types;
23mod validate;
24
25use crate::{
26    crate_path::{CratePathOverrides, rewrite_generated_paths},
27    node::{Def, ValidateNode},
28};
29use darling::{Error as DarlingError, FromMeta, ast::NestedMeta};
30use quote::quote;
31use syn::{ItemStruct, Visibility, parse_macro_input};
32
33/// Derive `Deref` for a one-field application wrapper.
34#[proc_macro_derive(Deref)]
35pub fn derive_deref(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
36    helper_deref::derive_deref(input.into()).into()
37}
38
39/// Derive `DerefMut` for a one-field application wrapper.
40#[proc_macro_derive(DerefMut)]
41pub fn derive_deref_mut(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
42    helper_deref::derive_deref_mut(input.into()).into()
43}
44
45///
46/// Prelude
47///
48/// Internal proc-macro prelude shared by node parsing and generator code.
49/// Keeps proc-macro essentials and schema helpers in one crate-local bundle.
50/// This is not part of the external derive surface.
51///
52
53mod prelude {
54    pub use crate::authoring_types::{Cardinality, Primitive};
55    pub use crate::{
56        r#gen::{Imp, Implementor},
57        helper::{quote_one, quote_option, quote_slice, to_path, to_str_lit},
58        node::*,
59        trait_kind::{TraitBuilder, TraitKind},
60        types::TraitStrategy,
61    };
62
63    pub const MAX_FIELD_NAME_LEN: usize = 64;
64
65    // proc-macro essentials
66    pub use darling::{Error as DarlingError, FromMeta};
67    pub use proc_macro2::{Span, TokenStream};
68    pub use quote::{ToTokens, format_ident, quote};
69    pub use serde::Deserialize;
70    pub use syn::{Ident, ItemStruct, LitStr, Path};
71}
72
73///
74/// Node Macros
75///
76
77macro_rules! macro_node {
78    ($fn_name:ident, $node_type:ty, $gen_type:path $(, $configure:expr)?) => {
79        #[proc_macro_attribute]
80        pub fn $fn_name(
81            args: proc_macro::TokenStream,
82            input: proc_macro::TokenStream,
83        ) -> proc_macro::TokenStream {
84            match NestedMeta::parse_meta_list(args.into()) {
85                Ok(mut args) => {
86                    let crate_paths = match CratePathOverrides::extract(&mut args) {
87                        Ok(crate_paths) => crate_paths,
88                        Err(err) => return err.write_errors().into(),
89                    };
90                    let item = parse_macro_input!(input as ItemStruct);
91
92                    // validate
93                    if !matches!(item.vis, Visibility::Public(_)) {
94                        return proc_macro::TokenStream::from(
95                            DarlingError::custom("expected public visibility").write_errors(),
96                        );
97                    }
98
99                    // build def
100                    let debug = item.attrs.iter().any(|attr| attr.path().is_ident("debug"));
101                    let mut node = match <$node_type>::from_list(&args) {
102                        Ok(node) => node,
103                        Err(err) => return proc_macro::TokenStream::from(err.write_errors()),
104                    };
105                    node.def = Def::new(item);
106                    $(($configure)(&mut node, CratePathOverrides::has_icydb_runtime());)?
107                    if let Err(err) = node.validate() {
108                        return proc_macro::TokenStream::from(err.write_errors());
109                    }
110
111                    // fatal schema errors
112                    let fatal_errors = node.fatal_errors();
113                    if !fatal_errors.is_empty() {
114                        let tokens: proc_macro2::TokenStream = fatal_errors
115                            .into_iter()
116                            .map(|err| err.to_compile_error())
117                            .collect();
118
119                        return tokens.into();
120                    }
121
122                    // instantiate the generator
123                    let generator = $gen_type(&node);
124                    let q = match rewrite_generated_paths(quote!(#generator), &crate_paths) {
125                        Ok(tokens) => tokens,
126                        Err(err) => return err.write_errors().into(),
127                    };
128
129                    if debug {
130                        quote! { compile_error!(stringify! { #q }); }
131                    } else {
132                        q
133                    }
134                    .into()
135                }
136                Err(e) => proc_macro::TokenStream::from(DarlingError::from(e).write_errors()),
137            }
138        }
139    };
140}
141
142macro_node!(canister, node::Canister, r#gen::CanisterGen);
143macro_node!(
144    entity,
145    node::Entity,
146    r#gen::EntityGen,
147    |entity: &mut node::Entity, has_runtime| entity.emit_runtime_adapters = has_runtime
148);
149macro_node!(enum_, node::Enum, r#gen::EnumGen);
150macro_node!(list, node::List, r#gen::ListGen);
151macro_node!(map, node::Map, r#gen::MapGen);
152macro_node!(newtype, node::Newtype, r#gen::NewtypeGen);
153macro_node!(record, node::Record, r#gen::RecordGen);
154macro_node!(normalizer, node::Normalizer, r#gen::NormalizerGen);
155macro_node!(set, node::Set, r#gen::SetGen);
156macro_node!(store, node::Store, r#gen::StoreGen);
157macro_node!(tuple, node::Tuple, r#gen::TupleGen);
158macro_node!(validator, node::Validator, r#gen::ValidatorGen);
159
160/// Derive addition for one-field application wrappers.
161#[proc_macro_derive(Add)]
162pub fn derive_add(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
163    finish_helper_derive(helper_ops::derive_add(input.into()))
164}
165
166/// Derive additive assignment for one-field application wrappers.
167#[proc_macro_derive(AddAssign)]
168pub fn derive_add_assign(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
169    finish_helper_derive(helper_ops::derive_add_assign(input.into()))
170}
171
172/// Derive subtraction for one-field application wrappers.
173#[proc_macro_derive(Sub)]
174pub fn derive_sub(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
175    finish_helper_derive(helper_ops::derive_sub(input.into()))
176}
177
178/// Derive subtractive assignment for one-field application wrappers.
179#[proc_macro_derive(SubAssign)]
180pub fn derive_sub_assign(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
181    finish_helper_derive(helper_ops::derive_sub_assign(input.into()))
182}
183
184/// Derive multiplication for one-field application wrappers.
185#[proc_macro_derive(Mul)]
186pub fn derive_mul(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
187    finish_helper_derive(helper_ops::derive_mul(input.into()))
188}
189
190/// Derive multiplicative assignment for one-field application wrappers.
191#[proc_macro_derive(MulAssign)]
192pub fn derive_mul_assign(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
193    finish_helper_derive(helper_ops::derive_mul_assign(input.into()))
194}
195
196/// Derive division for one-field application wrappers.
197#[proc_macro_derive(Div)]
198pub fn derive_div(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
199    finish_helper_derive(helper_ops::derive_div(input.into()))
200}
201
202/// Derive divisive assignment for one-field application wrappers.
203#[proc_macro_derive(DivAssign)]
204pub fn derive_div_assign(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
205    finish_helper_derive(helper_ops::derive_div_assign(input.into()))
206}
207
208/// Derive remainder for one-field application wrappers.
209#[proc_macro_derive(Rem)]
210pub fn derive_rem(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
211    finish_helper_derive(helper_ops::derive_rem(input.into()))
212}
213
214/// Derive iterator summation for one-field application wrappers.
215#[proc_macro_derive(Sum)]
216pub fn derive_sum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
217    finish_helper_derive(helper_ops::derive_sum(input.into()))
218}
219
220/// Derive display forwarding for one-field application wrappers.
221#[proc_macro_derive(Display)]
222pub fn derive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
223    finish_helper_derive(helper_display::derive_display(input.into()))
224}
225
226/// Derive borrowed and consuming inner access for one-field wrappers.
227#[proc_macro_derive(Inner)]
228pub fn derive_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
229    finish_helper_derive(helper_inner::derive_inner(input.into()))
230}
231
232fn finish_helper_derive(tokens: proc_macro2::TokenStream) -> proc_macro::TokenStream {
233    match rewrite_generated_paths(tokens, &CratePathOverrides::default()) {
234        Ok(tokens) => tokens.into(),
235        Err(err) => err.write_errors().into(),
236    }
237}