venndb_macros/
lib.rs

1#![forbid(unsafe_code)]
2
3mod errors;
4mod field;
5mod generate_db;
6mod parse_attrs;
7
8use errors::Errors;
9use field::StructField;
10use parse_attrs::{FieldAttrs, TypeAttrs};
11use proc_macro2::TokenStream;
12use quote::{ToTokens, format_ident, quote};
13
14/// Derive macro generating VennDB functionality for this struct.
15///
16/// See <https://docs.rs/venndb> for more information on how to use it.
17/// Or check out the README and usage tests in [the repository][repo] of this macro.
18///
19/// [repo]: https://github.com/plabayo/venndb
20#[proc_macro_derive(VennDB, attributes(venndb))]
21pub fn venndb(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
22    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
23    let ts: TokenStream = impl_from_args(&ast);
24    ts.into()
25}
26
27/// Transform the input into a token stream containing any generated implementations,
28/// as well as all errors that occurred.
29fn impl_from_args(input: &syn::DeriveInput) -> TokenStream {
30    let errors = &Errors::default();
31    let type_attrs = &TypeAttrs::parse(errors, input);
32    let mut output_tokens = match &input.data {
33        syn::Data::Struct(ds) => impl_from_args_struct(
34            errors,
35            &input.vis,
36            &input.ident,
37            type_attrs,
38            &input.generics,
39            ds,
40        ),
41        syn::Data::Enum(_) => {
42            errors.err(input, "`#[derive(VennDB)]` cannot be applied to enums");
43            TokenStream::new()
44        }
45        syn::Data::Union(_) => {
46            errors.err(input, "`#[derive(VennDB)]` cannot be applied to unions");
47            TokenStream::new()
48        }
49    };
50    errors.to_tokens(&mut output_tokens);
51    output_tokens
52}
53
54/// Implements `VennDB` for a `#[derive(VennDB)]` struct.
55fn impl_from_args_struct(
56    errors: &Errors,
57    vis: &syn::Visibility,
58    name: &syn::Ident,
59    type_attrs: &TypeAttrs,
60    _generic_args: &syn::Generics,
61    ds: &syn::DataStruct,
62) -> TokenStream {
63    let fields = match &ds.fields {
64        syn::Fields::Named(fields) => fields,
65        syn::Fields::Unnamed(_) => {
66            errors.err(
67                &ds.struct_token,
68                "`#![derive(VennDB)]` is not currently supported on tuple structs",
69            );
70            return TokenStream::new();
71        }
72        syn::Fields::Unit => {
73            errors.err(
74                &ds.struct_token,
75                "#![derive(VennDB)]` cannot be applied to unit structs",
76            );
77            return TokenStream::new();
78        }
79    };
80
81    let fields: Vec<_> = fields
82        .named
83        .iter()
84        .filter_map(|field| {
85            let attrs = FieldAttrs::parse(errors, field);
86            StructField::new(errors, field, attrs)
87        })
88        .collect();
89
90    let name_db = match &type_attrs.name {
91        Some(name) => format_ident!("{}", name.value()),
92        None => format_ident!("{}DB", name),
93    };
94
95    let db_code = generate_db::generate_db(
96        name,
97        &name_db,
98        type_attrs.validator.as_ref(),
99        vis,
100        &fields[..],
101    );
102
103    quote! {
104        #db_code
105    }
106}