exemplar_proc_macro/
lib.rs

1mod codegen;
2mod util;
3
4use proc_macro::TokenStream;
5use proc_macro_error2::*;
6
7use proc_macro2::Ident;
8use proc_macro2::Literal;
9use proc_macro2::TokenStream as QuoteStream;
10
11use quote::*;
12
13use syn::*;
14use syn::spanned::Spanned;
15
16use crate::util::Derivee;
17
18#[proc_macro_error]
19#[proc_macro_derive(
20    Model,
21    attributes(table, check, bind, extr, column)
22)]
23pub fn derive_model(input: TokenStream) -> TokenStream {
24    let ast = parse_macro_input!(input as DeriveInput);
25
26    let name = &ast.ident;
27    
28    if ast.generics.lt_token.is_some() {
29        abort_call_site!(
30            "Model can only be derived for concrete types.";
31            note = "Generics and lifetime parameters are not currently supported.";
32        )
33    }
34
35    let Data::Struct(data) = &ast.data else {
36        abort_call_site!(
37            "Model can only be derived for struct types.";
38            note = "Enums and unions are not supported...";
39            hint = "...but they can be embedded in a Model struct if they implement ToSql.";
40        )
41    };
42
43    let Fields::Named(fields) = &data.fields else {
44        abort_call_site!(
45            "Model can only be derived for structs with named fields.";
46            note = "Tuple and unit structs are not supported.";
47        )
48    };
49
50    let fields: Vec<_> = fields
51        .named
52        .iter()
53        .collect();
54
55    if fields.is_empty() {
56        abort_call_site!(
57            "Model can only be derived for structs with named fields.";
58            note = "Tuple and unit structs are not supported.";
59        )
60    }
61
62    let table  = util::get_table_name(&ast);
63    let schema = util::get_check_path(&ast);
64
65    let derivee = Derivee {
66        name: name.to_owned(),
67        table,
68        fields,
69        schema
70    };
71
72    let from_row   = codegen::from_row(&derivee);
73    let inserts    = codegen::inserts(&derivee);
74    let to_params  = codegen::to_params(&derivee);
75    let metadata   = codegen::metadata(&derivee);
76    let check_test = codegen::check_test(&derivee);
77    
78    quote! {
79        #[automatically_derived]
80        impl ::exemplar::Model for #name {
81            #from_row
82            #inserts
83            #to_params
84            #metadata
85        }
86
87        #[automatically_derived]
88        impl<'a> ::std::convert::TryFrom<&'a ::rusqlite::Row<'_>> for #name {
89            type Error = ::rusqlite::Error;
90
91            fn try_from(value: &'a ::rusqlite::Row) -> Result<Self, Self::Error> {
92                Self::from_row(value)
93            }
94        }
95
96        #check_test
97    }
98    .into()
99}
100