Skip to main content

armour_derive/
lib.rs

1#[macro_use]
2extern crate darling;
3extern crate proc_macro2;
4extern crate quote;
5extern crate syn;
6
7use proc_macro::TokenStream;
8
9use darling::util::Flag;
10use proc_macro2::TokenStream as TokenStream2;
11use syn::{Data, DeriveInput, Expr, ExprPath, LitStr, parse_macro_input};
12
13/// Adds metrics tracking to the function.
14///
15/// # Example
16/// ```rust,ignore
17/// #[armour_metrics(prefix = "my_prefix", name = tree.raw.name.clone())]
18/// fn my_function() {
19///     // function body
20/// }
21/// ```
22#[proc_macro_attribute]
23pub fn armour_metrics(attr: TokenStream, item: TokenStream) -> TokenStream {
24    metrics::armour_metrics_impl(attr, item)
25}
26
27mod crate_path;
28mod enum_parse;
29mod metrics;
30mod rapira_field_attrs;
31mod refs_parse;
32mod struct_parse;
33
34/// - `#[get_type(flatten)]` for unnamed structs
35/// - `#[get_type(with_type = Type)]` for named struct fields
36/// - `#[get_type(unimplemented)]` for named struct fields
37/// - `#[get_type(custom("name", &[Typ::Bool, Typ::Str]))]` for named struct fields
38#[proc_macro_derive(GetType, attributes(get_type, rapira, idx))]
39pub fn derive_get_type(stream: TokenStream) -> TokenStream {
40    let ast = parse_macro_input!(stream as DeriveInput);
41    if !ast.generics.params.is_empty() {
42        return syn::Error::new_spanned(
43            &ast.generics,
44            "#[derive(GetType)] does not support generic types",
45        )
46        .to_compile_error()
47        .into();
48    }
49    let name = &ast.ident;
50    let data = &ast.data;
51    let path = crate_path::schema_crate_path();
52
53    let get_type_impl: TokenStream2 = match data {
54        Data::Struct(data_struct) => struct_parse::parse(data_struct, name, &path),
55        Data::Enum(data_enum) => enum_parse::parse(data_enum, name, &path).into(),
56        Data::Union(_) => {
57            panic!(
58                "unions not supported, but Rust enums is implemented GetType trait (use Enums instead)"
59            )
60        }
61    };
62    // `derive(GetType)` does NOT emit `GetRefs`. Relation metadata is opt-in: add
63    // `#[derive(GetRefs)]` (typically `#[derive(GetType, GetRefs)]`) on the types
64    // that participate in the schema layer. This keeps `GetType` free of any
65    // `GetRefs` bound on fields, so types holding unbranded `Fuid<()>`, non-`IdBrand`
66    // hashers, or fields with a manual `GetType` impl keep compiling — regardless of
67    // what other crates in the build graph enable (no proc-macro feature to unify).
68    get_type_impl.into()
69}
70
71/// Standalone derive for relation metadata. Pair with `GetType`
72/// (`#[derive(GetType, GetRefs)]`) on stored types, or use alone on composite
73/// collection keys that don't implement `GetType`.
74#[proc_macro_derive(GetRefs, attributes(get_type))]
75pub fn derive_get_refs(stream: TokenStream) -> TokenStream {
76    let ast = parse_macro_input!(stream as DeriveInput);
77    if !ast.generics.params.is_empty() {
78        return syn::Error::new_spanned(
79            &ast.generics,
80            "#[derive(GetRefs)] does not support generic types",
81        )
82        .to_compile_error()
83        .into();
84    }
85    let path = crate_path::schema_crate_path();
86    refs_parse::generate(&ast.data, &ast.ident, &path).into()
87}
88
89#[derive(FromMeta, Debug)]
90struct CustomField {
91    name: LitStr,
92    types: Expr,
93}
94
95#[derive(Default, FromField, Debug)]
96#[darling(attributes(get_type))]
97pub(crate) struct FieldAttr {
98    pub(crate) with_type: Option<ExprPath>,
99    pub(crate) unimplemented: Flag,
100    pub(crate) custom: Option<CustomField>,
101    pub(crate) flatten: Flag,
102    pub(crate) no_refs: Flag,
103}