exemplar_proc_macro/
lib.rs1mod 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