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#[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
27fn 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
54fn 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}