1mod cols;
2mod column_trait;
3mod decode_column;
4mod decode_expression;
5mod decode_join;
6mod decode_table;
7mod encode_column_def;
8mod encode_column_ref;
9mod frag_evaluated;
10mod from_row_trait;
11
12use crate::{
13 cols::ColList,
14 decode_column::ColumnMetadata,
15 decode_table::{TableMetadata, decode_table},
16 encode_column_def::encode_column_def,
17 from_row_trait::from_row_trait,
18};
19use column_trait::column_trait;
20use decode_column::decode_column;
21use decode_expression::decode_expression;
22use decode_join::JoinParsed;
23use frag_evaluated::flag_evaluated;
24use proc_macro::TokenStream;
25use proc_macro2::TokenStream as TokenStream2;
26use quote::quote;
27use syn::{
28 Expr, Ident, Index, ItemStruct, parse_macro_input, parse2, punctuated::Punctuated,
29 token::AndAnd,
30};
31
32#[proc_macro_derive(Entity, attributes(tank))]
33pub fn derive_entity(input: TokenStream) -> TokenStream {
34 let table = decode_table(parse_macro_input!(input as ItemStruct));
35 let ident = &table.item.ident;
36 let name = &table.name;
37 let schema = &table.schema;
38 let metadata_and_filter = table
39 .columns
40 .iter()
41 .map(|metadata| {
42 let filter_passive = if let Some(ref filter_passive) = metadata.check_passive {
43 let field = &metadata.ident;
44 filter_passive(quote!(self.#field))
45 } else {
46 quote!(true)
47 };
48 (metadata, filter_passive)
49 })
50 .collect::<Vec<_>>();
51 let (from_row_factory, from_row) = from_row_trait(&table);
52 let primary_key_cols = table.primary_key.iter().map(|i| &table.columns[*i]);
53 let primary_key = primary_key_cols.clone().map(|col| {
54 let ident = &col.ident;
55 quote!(self.#ident)
56 });
57 let primary_keys_def = table.primary_key.iter().map(|i| quote!(&columns[#i]));
58 let unique_defs = &table
59 .unique
60 .iter()
61 .map(|v| {
62 if v.is_empty() {
63 quote!()
64 } else {
65 let i = v.iter();
66 quote!(vec![#(&columns[#i]),*].into_boxed_slice())
67 }
68 })
69 .collect::<Vec<_>>();
70 let unique_defs = quote!(vec![#(#unique_defs),*].into_boxed_slice());
71 let primary_key_types = primary_key_cols.clone().map(|col| col.ty.clone());
72 let (column_trait, column) = column_trait(&table);
73 let label_value_and_filter = metadata_and_filter.iter().map(|(column, filter)| {
74 let name = &column.name;
75 let field = &column.ident;
76 quote!((#name.into(), ::tank::AsValue::as_value(self.#field.clone()), #filter))
77 });
78 let row_full = metadata_and_filter.iter().map(
79 |(ColumnMetadata { ident, .. }, _)| quote!(::tank::AsValue::as_value(self.#ident.clone())),
80 );
81 let columns = metadata_and_filter.iter().map(|(c, _)| {
82 let field = &c.ident;
83 encode_column_def(&c, quote!(<#ident as #column_trait>::#field))
84 });
85 let primary_key_condition = table.primary_key.iter().enumerate().map(|(i, pki)| {
86 let ident = table.columns[*pki].ident.clone();
87 let span = ident.span();
88 (ident, Ident::new(&format!("pk{}", i), span))
89 });
90 let primary_key_condition_declaration = primary_key_condition
91 .clone()
92 .enumerate()
93 .clone()
94 .map(|(i, (_, pk))| {
95 let i = Index::from(i);
96 quote! { let #pk = primary_key.#i.to_owned(); }
97 })
98 .collect::<TokenStream2>();
99 let primary_key_condition_expression = primary_key_condition
100 .clone()
101 .map(|(field, pk)| quote!(#ident::#field == # #pk))
102 .collect::<Punctuated<_, AndAnd>>();
103 quote! {
104 #from_row
105 #column
106 impl ::tank::Entity for #ident {
107 type PrimaryKey<'a> = (#(&'a #primary_key_types,)*);
108
109 fn table() -> &'static ::tank::TableRef {
110 static TABLE: ::tank::TableRef = ::tank::TableRef {
111 name: ::std::borrow::Cow::Borrowed(#name),
112 schema: ::std::borrow::Cow::Borrowed(#schema),
113 alias: ::std::borrow::Cow::Borrowed(""),
114 };
115 &TABLE
116 }
117
118 fn columns() -> &'static [::tank::ColumnDef] {
119 static RESULT: ::std::sync::LazyLock<Box<[::tank::ColumnDef]>> =
120 ::std::sync::LazyLock::new(|| vec![#(#columns),*].into_boxed_slice());
121 &RESULT
122 }
123
124 fn primary_key_def() -> &'static [&'static ::tank::ColumnDef] {
125 static RESULT: ::std::sync::LazyLock<Box<[&'static ::tank::ColumnDef]>> =
126 ::std::sync::LazyLock::new(|| {
127 let columns = <#ident as ::tank::Entity>::columns();
128 vec![#(#primary_keys_def),*].into_boxed_slice()
129 });
130 &RESULT
131 }
132
133 fn primary_key<'a>(&'a self) -> Self::PrimaryKey<'a> {
134 (#(&#primary_key,)*)
135 }
136
137 fn unique_defs()
138 -> impl ExactSizeIterator<Item = impl ExactSizeIterator<Item = &'static ::tank::ColumnDef>> {
139 static RESULT: ::std::sync::LazyLock<Box<[Box<[&'static ::tank::ColumnDef]>]>> =
140 ::std::sync::LazyLock::new(|| {
141 let columns = #ident::columns();
142 #unique_defs
143 });
144 RESULT.iter().map(|v| v.iter().copied())
145 }
146
147 fn row_filtered(&self) -> Box<[(&'static str, ::tank::Value)]> {
148 [#(#label_value_and_filter),*]
149 .into_iter()
150 .filter_map(|(n, v, f)| if f { Some((n, v)) } else { None })
151 .collect()
152 }
153
154 fn row_full(&self) -> ::tank::Row {
155 [#(#row_full),*].into()
156 }
157
158 fn from_row(row: ::tank::RowLabeled) -> ::tank::Result<Self> {
159 #from_row_factory::<Self>::from_row(row)
160 }
161
162 fn find_pk(
163 executor: &mut impl ::tank::Executor,
164 primary_key: &Self::PrimaryKey<'_>,
165 ) -> impl ::std::future::Future<Output = ::tank::Result<Option<Self>>> {
166 async move {
167 #primary_key_condition_declaration
168 let condition = ::tank::expr!(#primary_key_condition_expression);
169 let query = ::tank::QueryBuilder::new()
170 .select(Self::columns())
171 .from(Self::table())
172 .where_condition(condition)
173 .limit(Some(1))
174 .build(&executor.driver());
175 let mut stream = ::tank::stream::StreamExt::boxed(executor.fetch(query));
177 ::tank::stream::StreamExt::next(&mut stream)
178 .await
179 .map(|v| v.and_then(Self::from_row))
180 .transpose()
181 }
182 }
183
184 fn delete_one(
185 executor: &mut impl ::tank::Executor,
186 primary_key: Self::PrimaryKey<'_>,
187 ) -> impl ::std::future::Future<Output = ::tank::Result<::tank::RowsAffected>> + Send
188 where
189 Self: Sized
190 {
191 #primary_key_condition_declaration
192 let condition = ::tank::expr!(#primary_key_condition_expression);
193 let mut query = ::tank::DynQuery::with_capacity(128);
194 ::tank::SqlWriter::write_delete::<Self>(
195 &::tank::Driver::sql_writer(&executor.driver()),
196 &mut query,
197 &condition,
198 );
199 executor.execute(query)
200 }
201 }
202 }
203 .into()
204}
205
206#[proc_macro]
207pub fn join(input: TokenStream) -> TokenStream {
233 let result = parse_macro_input!(input as JoinParsed);
234 result.0.into()
235}
236
237#[proc_macro]
238pub fn expr(input: TokenStream) -> TokenStream {
270 let mut input: TokenStream = flag_evaluated(input.into()).into();
271 if input.is_empty() {
272 input = quote!(false).into();
273 }
274 let expr = parse_macro_input!(input as Expr);
275 let parsed = decode_expression(&expr);
276 quote!(#parsed).into()
277}
278
279#[proc_macro]
280pub fn cols(input: TokenStream) -> TokenStream {
298 let input = flag_evaluated(input.into());
299 let Ok(ColList { cols: items }) = parse2(input) else {
300 panic!("Could not parse the columns");
301 };
302 let generated = items.iter().map(|item| {
303 let expr = &item.expr;
304 match &item.order {
305 Some(order) => {
306 quote! {
307 ::tank::Ordered {
308 order: #order,
309 expression: ::tank::expr!(#expr),
310 }
311 }
312 }
313 None => {
314 quote! { ::tank::expr!(#expr) }
315 }
316 }
317 });
318
319 TokenStream::from(quote! {
320 &[ #( &#generated as &dyn ::tank::Expression ),* ]
321 })
322}