1#![allow(unused)]
2#![allow(non_snake_case)]
3
4use ormlitex_attr::{ColumnAttributes, ColumnMetadata, TableMetadata, TableMetadataBuilder, ColumnMetadataBuilder, ModelAttributes, SyndecodeError, schema_from_filepaths, LoadOptions, ModelMetadata};
5use ormlitex_core::config::get_var_model_folders;
6use crate::codegen::common::ormlitexCodegen;
7use proc_macro::TokenStream;
8use std::borrow::Borrow;
9
10use quote::quote;
11use lazy_static::lazy_static;
12use syn::{Data, DeriveInput, Item, ItemStruct, parse_macro_input};
13use ormlitex_attr::DeriveInputExt;
14use std::collections::HashMap;
15use std::ops::Deref;
16use once_cell::sync::OnceCell;
17use ormlitex_core::Error::ormlitexError;
18use codegen::into_arguments::impl_IntoArguments;
19use crate::codegen::from_row::{impl_from_row_using_aliases, impl_FromRow};
20use crate::codegen::insert::impl_InsertModel;
21use crate::codegen::insert_model::struct_InsertModel;
22use crate::codegen::join_description::static_join_descriptions;
23use crate::codegen::meta::{impl_JoinMeta, impl_TableMeta};
24use crate::codegen::model::{impl_HasModelBuilder, impl_Model};
25use crate::codegen::model_builder::{impl_ModelBuilder, struct_ModelBuilder};
26
27mod util;
28mod codegen;
29
30pub(crate) type MetadataCache = HashMap<String, ModelMetadata>;
31
32static TABLES: OnceCell<MetadataCache> = OnceCell::new();
33
34fn get_tables() -> &'static MetadataCache {
35 TABLES.get_or_init(|| load_metadata_cache())
36}
37
38fn load_metadata_cache() -> MetadataCache {
39 let mut tables = HashMap::new();
40 let paths = get_var_model_folders();
41 let paths = paths.iter().map(|p| p.as_path()).collect::<Vec<_>>();
42 let schema = schema_from_filepaths(&paths).expect("Failed to preload models");
43 for meta in schema.tables {
44 let name = meta.struct_name().to_string();
45 tables.insert(name, meta);
46 }
47 tables
48}
49
50fn get_databases(table_meta: &TableMetadata) -> Vec<Box<dyn ormlitexCodegen>> {
52 let mut databases: Vec<Box<dyn ormlitexCodegen>> = Vec::new();
53 let dbs = table_meta.databases.clone();
54 if dbs.is_empty() {
55 #[cfg(feature = "default-sqlite")]
56 databases.push(Box::new(codegen::sqlite::SqliteBackend {}));
57 #[cfg(feature = "default-postgres")]
58 databases.push(Box::new(codegen::postgres::PostgresBackend {}));
59 #[cfg(feature = "default-mysql")]
60 databases.push(Box::new(codegen::mysql::MysqlBackend {}));
61 } else {
62 for db in dbs {
63 match db.as_str() {
64 #[cfg(feature = "sqlite")]
65 "sqlite" => databases.push(Box::new(codegen::sqlite::SqliteBackend {})),
66 #[cfg(feature = "postgres")]
67 "postgres" => databases.push(Box::new(codegen::postgres::PostgresBackend {})),
68 #[cfg(feature = "mysql")]
69 "mysql" => databases.push(Box::new(codegen::mysql::MysqlBackend {})),
70 "sqlite" | "postgres" | "mysql" => panic!("Database {} is not enabled. Enable it with features = [\"{}\"]", db, db),
71 _ => panic!("Unknown database: {}", db),
72 }
73 }
74 }
75 if databases.is_empty() {
76 let mut count = 0;
77 #[cfg(feature = "sqlite")]
78 {
79 count += 1;
80 }
81 #[cfg(feature = "postgres")]
82 {
83 count += 1;
84 }
85 #[cfg(feature = "mysql")]
86 {
87 count += 1;
88 }
89 if count > 1 {
90 panic!("You have more than one database configured using features, but no database is specified for this model. \
91 Specify a database for the model like this:\n\n#[ormlitex(database = \"<db>\")]\n\nOr you can enable \
92 a default database feature:\n\n # Cargo.toml\normlitex = {{ features = [\"default-<db>\"] }}");
93 }
94 }
95 if databases.is_empty() {
96 #[cfg(feature = "sqlite")]
97 databases.push(Box::new(codegen::sqlite::SqliteBackend {}));
98 #[cfg(feature = "postgres")]
99 databases.push(Box::new(codegen::postgres::PostgresBackend {}));
100 #[cfg(feature = "mysql")]
101 databases.push(Box::new(codegen::mysql::MysqlBackend {}));
102 }
103 databases
104}
105
106#[proc_macro_derive(Model, attributes(ormlitex))]
109pub fn expand_ormlitex_model(input: TokenStream) -> TokenStream {
110 let ast = parse_macro_input!(input as DeriveInput);
111 let Data::Struct(data) = &ast.data else { panic!("Only structs can derive Model"); };
112
113 let meta = ModelMetadata::from_derive(&ast).expect("Failed to parse table metadata");
114
115 let mut databases = get_databases(&meta.inner);
116 let tables = get_tables();
117
118 let first = databases.remove(0);
119
120 let primary = {
121 let db = first.as_ref();
122 let impl_TableMeta = impl_TableMeta(&meta.inner, Some(meta.pkey.column_name.as_str()));
123 let impl_JoinMeta = impl_JoinMeta(&meta);
124 let static_join_descriptions = static_join_descriptions(&meta.inner, &tables);
125 let impl_Model = impl_Model(db, &meta, tables);
126 let impl_HasModelBuilder = impl_HasModelBuilder(db, &meta);
127 let impl_FromRow = impl_FromRow(&meta.inner, &tables);
128 let impl_from_row_using_aliases = impl_from_row_using_aliases(&meta.inner, &tables);
129
130 let struct_ModelBuilder = struct_ModelBuilder(&ast, &meta);
131 let impl_ModelBuilder = impl_ModelBuilder(db, &meta);
132
133 let struct_InsertModel = struct_InsertModel(&ast, &meta);
134 let impl_InsertModel = impl_InsertModel(db, &meta);
135
136 quote! {
137 #impl_TableMeta
138 #impl_JoinMeta
139
140 #static_join_descriptions
141 #impl_Model
142 #impl_HasModelBuilder
143 #impl_FromRow
144 #impl_from_row_using_aliases
145
146 #struct_ModelBuilder
147 #impl_ModelBuilder
148
149 #struct_InsertModel
150 #impl_InsertModel
151 }
152 };
153
154 let rest = databases.iter().map(|db| {
155 let impl_Model = impl_Model(db.as_ref(), &meta, tables);
156 quote! {
157 #impl_Model
158 }
159 });
160
161 TokenStream::from(quote! {
162 #primary
163 #(#rest)*
164 })
165}
166
167#[proc_macro_derive(FromRow, attributes(ormlitex))]
168pub fn expand_derive_fromrow(input: TokenStream) -> TokenStream {
169 let ast = parse_macro_input!(input as DeriveInput);
170 let Data::Struct(data) = &ast.data else { panic!("Only structs can derive Model"); };
171
172 let meta = TableMetadata::from_derive(&ast).unwrap();
173
174 let databases = get_databases(&meta);
175 let tables = get_tables();
176
177 let expanded = databases.iter().map(|db| {
178 let impl_FromRow = impl_FromRow(&meta, &tables);
179 let impl_from_row_using_aliases = impl_from_row_using_aliases(&meta, &tables);
180 quote! {
181 #impl_FromRow
182 #impl_from_row_using_aliases
183 }
184 });
185
186 TokenStream::from(quote! {
187 #(#expanded)*
188 })
189}
190
191#[proc_macro_derive(TableMeta, attributes(ormlitex))]
192pub fn expand_derive_table_meta(input: TokenStream) -> TokenStream {
193 let ast = parse_macro_input!(input as DeriveInput);
194 let Data::Struct(data) = &ast.data else { panic!("Only structs can derive Model"); };
195
196 let table_meta = TableMetadata::from_derive(&ast).expect("Failed to parse table metadata");
197 let databases = get_databases(&table_meta);
198 let db = databases.first().expect("No database configured");
199
200 let impl_TableMeta = impl_TableMeta(&table_meta, table_meta.pkey.as_ref().map(|pkey| pkey.as_str()));
201 TokenStream::from(impl_TableMeta)
202}
203
204#[proc_macro_derive(IntoArguments, attributes(ormlitex))]
205pub fn expand_derive_into_arguments(input: TokenStream) -> TokenStream {
206 let ast = parse_macro_input!(input as DeriveInput);
207 let Data::Struct(data) = &ast.data else { panic!("Only structs can derive Model"); };
208
209 let meta = TableMetadata::from_derive(&ast).unwrap();
210 let databases = get_databases(&meta);
211
212 let expanded = databases.iter().map(|db| {
213 let impl_IntoArguments = impl_IntoArguments(db.as_ref(), &meta);
214 impl_IntoArguments
215 });
216 TokenStream::from(quote! {
217 #(#expanded)*
218 })
219}
220
221#[proc_macro_derive(ManualType)]
226pub fn expand_derive_manual_type(input: TokenStream) -> TokenStream {
227 TokenStream::new()
228}