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}