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};
31use tank_core::PrimaryKeyType;
32
33#[proc_macro_derive(Entity, attributes(tank))]
34pub fn derive_entity(input: TokenStream) -> TokenStream {
35 let table = decode_table(parse_macro_input!(input as ItemStruct));
36 let ident = &table.item.ident;
37 let name = &table.name;
38 let schema = &table.schema;
39 let metadata_and_filter = table
40 .columns
41 .iter()
42 .map(|metadata| {
43 let filter_passive = if let Some(ref filter_passive) = metadata.check_passive {
44 let field = &metadata.ident;
45 filter_passive(quote!(self.#field))
46 } else {
47 quote!(true)
48 };
49 (metadata, filter_passive)
50 })
51 .collect::<Vec<_>>();
52 let (from_row_factory, from_row) = from_row_trait(&table);
53 let primary_keys: Vec<_> = metadata_and_filter
54 .iter()
55 .enumerate()
56 .filter_map(|(i, (m, ..))| {
57 if matches!(
58 m.primary_key,
59 PrimaryKeyType::PrimaryKey | PrimaryKeyType::PartOfPrimaryKey
60 ) {
61 Some((i, m))
62 } else {
63 None
64 }
65 })
66 .collect();
67 let primary_key = primary_keys
68 .iter()
69 .map(|(_i, c)| c.ident.clone())
70 .map(|ident| quote!(self.#ident));
71 let primary_key_def = primary_keys.iter().map(|(i, _)| quote!(columns[#i]));
72 let unique_defs = &table
73 .unique
74 .iter()
75 .map(|v| {
76 if v.is_empty() {
77 quote!()
78 } else {
79 let i = v.iter();
80 quote!(vec![#(&columns[#i]),*].into_boxed_slice())
81 }
82 })
83 .collect::<Vec<_>>();
84 let unique_defs = quote!(vec![#(#unique_defs),*].into_boxed_slice());
85 let primary_key_types = primary_keys.iter().map(|(_, c)| c.ty.clone());
86 let column = column_trait(&table);
87 let label_value_and_filter = metadata_and_filter.iter().map(|(column, filter)| {
88 let name = &column.name;
89 let field = &column.ident;
90 quote!((#name.into(), ::tank::AsValue::as_value(self.#field.clone()), #filter))
91 });
92 let row_full = metadata_and_filter.iter().map(
93 |(ColumnMetadata { ident, .. }, _)| quote!(::tank::AsValue::as_value(self.#ident.clone())),
94 );
95 let columns = metadata_and_filter.iter().map(|(c, _)| {
96 let field = &c.ident;
97 encode_column_def(&c, quote!(#ident::#field))
98 });
99 let primary_key_condition = primary_keys.iter().enumerate().map(|(i, (_, c))| {
100 (
101 &c.ident,
102 Index::from(i),
103 Ident::new(&format!("pk{}", i), c.ident.span()),
104 )
105 });
106 let primary_key_condition_declaration = primary_key_condition
107 .clone()
108 .map(|(_, i, pk)| quote! { let #pk = primary_key.#i.to_owned(); })
109 .collect::<TokenStream2>();
110 let primary_key_condition_expression = primary_key_condition
111 .clone()
112 .map(|(field, _i, pk)| quote!(#ident::#field == # #pk))
113 .collect::<Punctuated<_, AndAnd>>();
114 quote! {
115 #from_row
116 #column
117 impl ::tank::Entity for #ident {
118 type PrimaryKey<'a> = (#(&'a #primary_key_types,)*);
119
120 fn table() -> &'static ::tank::TableRef {
121 static TABLE: ::tank::TableRef = ::tank::TableRef {
122 name: #name,
123 schema: #schema,
124 alias: ::std::borrow::Cow::Borrowed(""),
125 };
126 &TABLE
127 }
128
129 fn columns() -> &'static [::tank::ColumnDef] {
130 static RESULT: ::std::sync::LazyLock<Box<[::tank::ColumnDef]>> =
131 ::std::sync::LazyLock::new(|| vec![#(#columns),*].into_boxed_slice());
132 &RESULT
133 }
134
135 fn primary_key_def() -> impl ExactSizeIterator<Item = &'static ::tank::ColumnDef> {
136 static RESULT: ::std::sync::LazyLock<Box<[&::tank::ColumnDef]>> =
137 ::std::sync::LazyLock::new(|| {
138 let columns = #ident::columns();
139 vec![#(&#primary_key_def),*].into_boxed_slice()
140 });
141 RESULT.iter().copied()
142 }
143
144 fn primary_key<'a>(&'a self) -> Self::PrimaryKey<'a> {
145 (#(&#primary_key,)*)
146 }
147
148 fn unique_defs()
149 -> impl ExactSizeIterator<Item = impl ExactSizeIterator<Item = &'static ::tank::ColumnDef>> {
150 static RESULT: ::std::sync::LazyLock<Box<[Box<[&'static ::tank::ColumnDef]>]>> =
151 ::std::sync::LazyLock::new(|| {
152 let columns = #ident::columns();
153 #unique_defs
154 });
155 RESULT.iter().map(|v| v.iter().copied())
156 }
157
158 fn row_filtered(&self) -> Box<[(&'static str, ::tank::Value)]> {
159 [#(#label_value_and_filter),*]
160 .into_iter()
161 .filter_map(|(n, v, f)| if f { Some((n, v)) } else { None })
162 .collect()
163 }
164
165 fn row_full(&self) -> ::tank::Row {
166 [#(#row_full),*].into()
167 }
168
169 fn from_row(row: ::tank::RowLabeled) -> ::tank::Result<Self> {
170 #from_row_factory::<Self>::from_row(row)
171 }
172
173 async fn create_table(
174 executor: &mut impl ::tank::Executor,
175 if_not_exists: bool,
176 create_schema: bool,
177 ) -> ::tank::Result<()> {
178 let mut query = String::with_capacity(2048);
179 if create_schema && !#schema.is_empty() {
180 ::tank::SqlWriter::write_create_schema::<#ident>(
181 &::tank::Driver::sql_writer(executor.driver()),
182 &mut query,
183 true,
184 );
185 }
186 ::tank::SqlWriter::write_create_table::<#ident>(
187 &::tank::Driver::sql_writer(executor.driver()),
188 &mut query,
189 if_not_exists,
190 );
191 ::tank::future::FutureExt::boxed(executor.execute(query))
193 .await
194 .map(|_| ())
195 }
196
197 async fn drop_table(
198 executor: &mut impl ::tank::Executor,
199 if_exists: bool,
200 drop_schema: bool,
201 ) -> ::tank::Result<()> {
202 let mut query = String::with_capacity(256);
203 ::tank::SqlWriter::write_drop_table::<#ident>(
204 &::tank::Driver::sql_writer(executor.driver()),
205 &mut query,
206 if_exists,
207 );
208 if drop_schema && !#schema.is_empty() {
209 ::tank::SqlWriter::write_drop_schema::<#ident>(
210 &::tank::Driver::sql_writer(executor.driver()),
211 &mut query,
212 true,
213 );
214 }
215 ::tank::future::FutureExt::boxed(executor.execute(query))
217 .await
218 .map(|_| ())
219 }
220
221 fn insert_one(
222 executor: &mut impl ::tank::Executor,
223 entity: &impl ::tank::Entity,
224 ) -> impl ::std::future::Future<Output = ::tank::Result<::tank::RowsAffected>> + Send {
225 let mut query = String::with_capacity(128);
226 ::tank::SqlWriter::write_insert(
227 &::tank::Driver::sql_writer(executor.driver()),
228 &mut query,
229 [entity],
230 false,
231 );
232 executor.execute(query)
233 }
234
235 fn insert_many<'a, It>(
236 executor: &mut impl ::tank::Executor,
237 entities: It,
238 ) -> impl ::std::future::Future<Output = ::tank::Result<::tank::RowsAffected>> + Send
239 where
240 Self: 'a,
241 It: IntoIterator<Item = &'a Self> + Send,
242 {
243 executor.append(entities)
244 }
245
246 fn find_pk(
247 executor: &mut impl ::tank::Executor,
248 primary_key: &Self::PrimaryKey<'_>,
249 ) -> impl ::std::future::Future<Output = ::tank::Result<Option<Self>>> {
250 async move {
251 #primary_key_condition_declaration
252 let condition = ::tank::expr!(#primary_key_condition_expression);
253 let stream = ::tank::DataSet::select(
254 Self::table(),
255 executor,
256 Self::columns()
257 .iter()
258 .map(|c| &c.column_ref as &dyn ::tank::Expression),
259 &condition,
260 Some(1),
261 );
262 let mut stream = ::tank::stream::StreamExt::boxed(stream);
264 ::tank::stream::StreamExt::next(&mut stream)
265 .await
266 .map(|v| v.and_then(Self::from_row))
267 .transpose()
268 }
269 }
270
271 fn find_many(
272 executor: &mut impl ::tank::Executor,
273 condition: &impl ::tank::Expression,
274 limit: Option<u32>,
275 ) -> impl ::tank::stream::Stream<Item = ::tank::Result<Self>> {
276 ::tank::stream::StreamExt::map(
277 ::tank::DataSet::select(
278 Self::table(),
279 executor,
280 Self::columns()
281 .iter()
282 .map(|c| &c.column_ref as &dyn ::tank::Expression),
283 condition,
284 limit,
285 ),
286 |result| result.and_then(Self::from_row),
287 )
288 }
289
290 fn delete_one(
291 executor: &mut impl ::tank::Executor,
292 primary_key: Self::PrimaryKey<'_>,
293 ) -> impl ::std::future::Future<Output = ::tank::Result<::tank::RowsAffected>> + Send
294 where
295 Self: Sized
296 {
297 #primary_key_condition_declaration
298 let condition = ::tank::expr!(#primary_key_condition_expression);
299 let mut query = String::with_capacity(128);
300 ::tank::SqlWriter::write_delete::<Self>(
301 &::tank::Driver::sql_writer(executor.driver()),
302 &mut query,
303 &condition,
304 );
305 executor.execute(query)
306 }
307
308 fn delete_many(
309 executor: &mut impl ::tank::Executor,
310 condition: &impl ::tank::Expression,
311 ) -> impl ::std::future::Future<Output = ::tank::Result<::tank::RowsAffected>> + Send
312 where
313 Self: Sized
314 {
315 let mut query = String::with_capacity(128);
316 ::tank::SqlWriter::write_delete::<Self>(
317 &::tank::Driver::sql_writer(executor.driver()),
318 &mut query,
319 condition,
320 );
321 executor.execute(query)
322 }
323 }
324 }
325 .into()
326}
327
328#[proc_macro]
329pub fn join(input: TokenStream) -> TokenStream {
355 let result = parse_macro_input!(input as JoinParsed);
356 result.0.into()
357}
358
359#[proc_macro]
360pub fn expr(input: TokenStream) -> TokenStream {
392 let mut input: TokenStream = flag_evaluated(input.into()).into();
393 if input.is_empty() {
394 input = quote!(false).into();
395 }
396 let expr = parse_macro_input!(input as Expr);
397 let parsed = decode_expression(&expr);
398 quote!(#parsed).into()
399}
400
401#[proc_macro]
402pub fn cols(input: TokenStream) -> TokenStream {
420 let input = flag_evaluated(input.into());
421 let Ok(ColList { cols: items }) = parse2(input) else {
422 panic!("Could not parse the columns");
423 };
424 let generated = items.iter().map(|item| {
425 let expr = &item.expr;
426 match &item.order {
427 Some(order) => {
428 quote! {
429 ::tank::Ordered {
430 order: #order,
431 expression: ::tank::expr!(#expr),
432 }
433 }
434 }
435 None => {
436 quote! { ::tank::expr!(#expr) }
437 }
438 }
439 });
440
441 TokenStream::from(quote! {
442 &[ #( &#generated as &dyn ::tank::Expression ),* ]
443 })
444}