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