rust_query_macros/
lib.rs

1use dummy::{dummy_impl, from_expr};
2use heck::{ToSnekCase, ToUpperCamelCase};
3use multi::{SingleVersionTable, VersionedSchema};
4use proc_macro2::TokenStream;
5use quote::{format_ident, quote};
6use syn::{Ident, ItemMod, ItemStruct};
7use table::define_all_tables;
8
9mod dummy;
10mod migrations;
11mod multi;
12mod parse;
13mod table;
14mod unique;
15
16#[proc_macro_attribute]
17pub fn schema(
18    attr: proc_macro::TokenStream,
19    item: proc_macro::TokenStream,
20) -> proc_macro::TokenStream {
21    let name = syn::parse_macro_input!(attr as syn::Ident);
22    let item = syn::parse_macro_input!(item as ItemMod);
23
24    match generate(name, item) {
25        Ok(x) => x,
26        Err(e) => e.into_compile_error(),
27    }
28    .into()
29}
30
31#[proc_macro_derive(Select)]
32pub fn from_row(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
33    let item = syn::parse_macro_input!(item as ItemStruct);
34    match dummy_impl(item) {
35        Ok(x) => x,
36        Err(e) => e.into_compile_error(),
37    }
38    .into()
39}
40
41#[proc_macro_derive(FromExpr, attributes(rust_query))]
42pub fn from_expr_macro(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
43    let item = syn::parse_macro_input!(item as ItemStruct);
44    match from_expr(item) {
45        Ok(x) => x,
46        Err(e) => e.into_compile_error(),
47    }
48    .into()
49}
50
51fn make_generic(name: &Ident) -> Ident {
52    let normalized = name.to_string().to_upper_camel_case();
53    format_ident!("_{normalized}")
54}
55
56fn to_lower(name: &Ident) -> Ident {
57    let normalized = name.to_string().to_snek_case();
58    format_ident!("{normalized}")
59}
60
61fn generate(schema_name: Ident, item: syn::ItemMod) -> syn::Result<TokenStream> {
62    let schema = VersionedSchema::parse(item)?;
63
64    let mut output = quote! {};
65    let mut prev_mod = None;
66
67    let mut iter = schema
68        .versions
69        .clone()
70        .map(|version| Ok((version, schema.get(version)?)))
71        .collect::<syn::Result<Vec<_>>>()?
72        .into_iter()
73        .peekable();
74
75    while let Some((version, mut new_tables)) = iter.next() {
76        let next_mod = iter
77            .peek()
78            .map(|(peek_version, _)| format_ident!("v{peek_version}"));
79        let mut mod_output =
80            define_all_tables(&schema_name, &prev_mod, &next_mod, version, &mut new_tables)?;
81
82        let new_mod = format_ident!("v{version}");
83
84        if let Some((peek_version, peek_tables)) = iter.peek() {
85            let peek_mod = format_ident!("v{peek_version}");
86            let m = migrations::migrations(
87                &schema_name,
88                new_tables,
89                peek_tables,
90                quote! {super},
91                quote! {super::super::#peek_mod},
92            )?;
93            mod_output.extend(quote! {
94                pub mod migrate {
95                    #m
96                }
97            });
98        }
99
100        output.extend(quote! {
101            pub mod #new_mod {
102                #mod_output
103            }
104        });
105
106        prev_mod = Some(new_mod);
107    }
108
109    Ok(output)
110}