Skip to main content

sqlx_models_derive/
lib.rs

1extern crate proc_macro;
2use convert_case::{Case, Casing};
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::__private::TokenStream2;
6use syn::parse::{Parse, ParseStream, Result};
7use syn::{
8    braced, parenthesized, parse_macro_input, parse_str, punctuated::Punctuated, token::Comma,
9    Attribute, BareFnArg, Field, Fields, Ident, ItemStruct, LitStr, Path, PathArguments, Token,
10    Type, TypePath,
11};
12
13mod kw {
14    syn::custom_keyword!(table);
15    syn::custom_keyword!(state);
16    syn::custom_keyword!(queries);
17    syn::custom_keyword!(has_many);
18    syn::custom_keyword!(belongs_to);
19    syn::custom_keyword!(default);
20    syn::custom_keyword!(no_update);
21    syn::custom_keyword!(no_insert);
22    syn::custom_keyword!(no_delete);
23}
24
25#[derive(Debug)]
26struct Query {
27    method_name: Ident,
28    sql: LitStr,
29    args: Punctuated<BareFnArg, Comma>,
30}
31
32impl Parse for Query {
33    fn parse(input: ParseStream) -> Result<Self> {
34        let method_name: Ident = input.parse()?;
35        let content;
36        parenthesized!(content in input);
37        let sql: LitStr = content.parse()?;
38        let args: Punctuated<BareFnArg, Comma> = match content.parse::<Token![,]>() {
39            Ok(_) => content.parse_terminated(BareFnArg::parse)?,
40            _ => Punctuated::new(),
41        };
42        Ok(Query {
43            method_name,
44            sql,
45            args,
46        })
47    }
48}
49
50#[derive(Debug)]
51struct Association {
52    model_name: Ident,
53    column_name: Ident,
54}
55
56impl Parse for Association {
57    fn parse(input: ParseStream) -> Result<Self> {
58        let model_name: Ident = input.parse()?;
59        let content;
60        parenthesized!(content in input);
61        let column_name: Ident = content.parse()?;
62        Ok(Association {
63            model_name,
64            column_name,
65        })
66    }
67}
68
69#[derive(Debug)]
70enum ModelConfig {
71    Queries(Punctuated<Query, Comma>),
72    HasMany(Punctuated<Association, Comma>),
73    BelongsTo(Punctuated<Association, Comma>),
74}
75
76impl Parse for ModelConfig {
77    fn parse(input: ParseStream) -> Result<Self> {
78        if input.peek(kw::queries) {
79            let _ = input.parse::<kw::queries>()?;
80            let content;
81            braced!(content in input);
82            let queries = content.parse_terminated(Query::parse)?;
83            Ok(ModelConfig::Queries(queries))
84        } else if input.peek(kw::has_many) {
85            let _ = input.parse::<kw::has_many>()?;
86            let content;
87            braced!(content in input);
88            let associations = content.parse_terminated(Association::parse)?;
89            Ok(ModelConfig::HasMany(associations))
90        } else if input.peek(kw::belongs_to) {
91            let _ = input.parse::<kw::belongs_to>()?;
92            let content;
93            braced!(content in input);
94            let associations = content.parse_terminated(Association::parse)?;
95            Ok(ModelConfig::BelongsTo(associations))
96        } else {
97            panic!("Unexpected model config name");
98        }
99    }
100}
101
102#[derive(Debug)]
103struct ModelHints {
104    ty: Ident,
105    default: bool,
106    op_ne: bool,
107    op_gt: bool,
108    op_gte: bool,
109    op_lt: bool,
110    op_lte: bool,
111    op_like: bool,
112    op_not_like: bool,
113    op_ilike: bool,
114    op_not_ilike: bool,
115    op_similar_to: bool,
116    op_not_similar_to: bool,
117    op_in: bool,
118    op_not_in: bool,
119    op_is_set: bool,
120}
121
122impl Parse for ModelHints {
123    fn parse(input: ParseStream) -> Result<Self> {
124        let ty: Ident = input.parse()?;
125
126        let mut hints = ModelHints {
127            ty,
128            default: false,
129            op_ne: false,
130            op_gt: false,
131            op_gte: false,
132            op_lt: false,
133            op_lte: false,
134            op_like: false,
135            op_not_like: false,
136            op_ilike: false,
137            op_not_ilike: false,
138            op_similar_to: false,
139            op_not_similar_to: false,
140            op_in: false,
141            op_not_in: false,
142            op_is_set: false,
143        };
144
145        while !input.is_empty() {
146            // Consume a comma if there are more tokens
147            if input.peek(Token![,]) {
148                input.parse::<Token![,]>()?;
149            } else {
150                break;
151            }
152
153            match input.parse::<Ident>()?.to_string().as_str() {
154                "default" => hints.default = true,
155                "all_ops" => {
156                    hints.op_ne = true;
157                    hints.op_gt = true;
158                    hints.op_gte = true;
159                    hints.op_lt = true;
160                    hints.op_lte = true;
161                    hints.op_like = true;
162                    hints.op_not_like = true;
163                    hints.op_ilike = true;
164                    hints.op_not_ilike = true;
165                    hints.op_similar_to = true;
166                    hints.op_not_similar_to = true;
167                    hints.op_in = true;
168                    hints.op_not_in = true;
169                    hints.op_is_set = true;
170                }
171                "op_ne" => hints.op_ne = true,
172                "op_gt" => hints.op_gt = true,
173                "op_gte" => hints.op_gte = true,
174                "op_lt" => hints.op_lt = true,
175                "op_lte" => hints.op_lte = true,
176                "op_like" => hints.op_like = true,
177                "op_not_like" => hints.op_not_like = true,
178                "op_ilike" => hints.op_ilike = true,
179                "op_not_ilike" => hints.op_not_ilike = true,
180                "op_similar_to" => hints.op_similar_to = true,
181                "op_not_similar_to" => hints.op_not_similar_to = true,
182                "op_in" => hints.op_in = true,
183                "op_not_in" => hints.op_not_in = true,
184                "op_is_set" => hints.op_is_set = true,
185                other => {
186                    panic!("Unknown flag for field {other}")
187                }
188            }
189        }
190
191        Ok(hints)
192    }
193}
194
195#[derive(Debug)]
196struct SqlxModelConf {
197    id_type: Type,
198    struct_name: Ident,
199    extra_struct_attributes: Vec<Attribute>,
200    attrs_struct: Ident,
201    state_name: Ident,
202    table_name: Ident,
203    fields: Punctuated<Field, Comma>,
204    queries: Punctuated<Query, Comma>,
205    has_many: Punctuated<Association, Comma>,
206    belongs_to: Punctuated<Association, Comma>,
207    hub_struct: Ident,
208    sql_select_columns: String,
209    field_idents: Vec<Ident>,
210    hub_builder_method: Ident,
211    no_update: bool,
212    no_insert: bool,
213    no_delete: bool,
214}
215
216impl Parse for SqlxModelConf {
217    fn parse(input: ParseStream) -> Result<Self> {
218        let _ = input.parse::<kw::state>()?;
219        input.parse::<Token![:]>()?;
220        let state_name: Ident = input.parse()?;
221        input.parse::<Token![,]>()?;
222        let _ = input.parse::<kw::table>()?;
223        input.parse::<Token![:]>()?;
224        let table_name: Ident = input.parse()?;
225        input.parse::<Token![,]>()?;
226
227        let no_update = if input.peek(kw::no_update) {
228            input.parse::<kw::no_update>()?;
229            input.parse::<Token![,]>()?;
230            true
231        } else {
232            false
233        };
234
235        let no_insert = if input.peek(kw::no_insert) {
236            input.parse::<kw::no_insert>()?;
237            input.parse::<Token![,]>()?;
238            true
239        } else {
240            false
241        };
242
243        let no_delete = if input.peek(kw::no_delete) {
244            input.parse::<kw::no_delete>()?;
245            input.parse::<Token![,]>()?;
246            true
247        } else {
248            false
249        };
250
251        let whole_struct: ItemStruct = input.parse()?;
252
253        let struct_name: Ident = whole_struct.ident.clone();
254        let named_fields = match whole_struct.fields.clone() {
255            Fields::Named(x) => x,
256            _ => panic!("Struct needs named fields"),
257        };
258
259        let mut queries: Punctuated<Query, Comma> = Punctuated::new();
260        let mut has_many: Punctuated<Association, Comma> = Punctuated::new();
261        let mut belongs_to: Punctuated<Association, Comma> = Punctuated::new();
262
263        if input.parse::<Token![,]>().is_ok() {
264            let configs: Punctuated<ModelConfig, Comma> =
265                input.parse_terminated(ModelConfig::parse)?;
266            for config in configs {
267                match config {
268                    ModelConfig::Queries(a) => queries = a,
269                    ModelConfig::HasMany(a) => has_many = a,
270                    ModelConfig::BelongsTo(a) => belongs_to = a,
271                }
272            }
273        }
274
275        let extra_struct_attributes = whole_struct.attrs.clone();
276
277        let attrs_struct = format_ident!("{}Attrs", &struct_name);
278
279        let fields = named_fields.named;
280
281        let hub_struct = format_ident!("{}Hub", struct_name);
282        let sql_select_columns = fields
283            .iter()
284            .map(|f| {
285                let name = f.ident.as_ref().unwrap();
286                let ty = &f.ty;
287                format!(r#"{} as "{}!: {}""#, name, name, quote! { #ty })
288            })
289            .collect::<Vec<String>>()
290            .join(", \n");
291
292        let field_idents: Vec<Ident> = fields
293            .clone()
294            .into_iter()
295            .map(|i| i.ident.unwrap())
296            .collect();
297
298        let id_type = fields
299            .iter()
300            .find(|i| i.ident.as_ref().unwrap() == "id")
301            .expect("struct to have an id field")
302            .ty
303            .clone();
304
305        let hub_builder_method = Ident::new(
306            &struct_name.to_string().to_case(Case::Snake),
307            struct_name.span(),
308        );
309
310        Ok(SqlxModelConf {
311            id_type,
312            extra_struct_attributes,
313            state_name,
314            struct_name,
315            attrs_struct,
316            table_name,
317            fields,
318            queries,
319            has_many,
320            belongs_to,
321            hub_struct,
322            sql_select_columns,
323            field_idents,
324            hub_builder_method,
325            no_update,
326            no_insert,
327            no_delete,
328        })
329    }
330}
331
332#[proc_macro]
333pub fn model(tokens: TokenStream) -> TokenStream {
334    let conf = parse_macro_input!(tokens as SqlxModelConf);
335    let state_name = &conf.state_name;
336    let hub_struct = &conf.hub_struct;
337    let hub_builder_method = &conf.hub_builder_method;
338
339    let base_section = build_base(&conf);
340    let select_section = build_select(&conf);
341    let insert_section = if conf.no_insert {
342        quote! {}
343    } else {
344        build_insert(&conf)
345    };
346    let update_section = if conf.no_update {
347        quote! {}
348    } else {
349        build_update(&conf)
350    };
351    let delete_section = if conf.no_delete {
352        quote! {}
353    } else {
354        build_delete(&conf)
355    };
356    let queries_section = build_queries(&conf);
357
358    let quoted = quote! {
359      pub struct #hub_struct {
360        state: #state_name,
361      }
362
363      impl #state_name {
364        pub fn #hub_builder_method(&self) -> #hub_struct {
365          #hub_struct::new(self.clone())
366        }
367      }
368
369      impl #hub_struct {
370        pub fn new(state: #state_name) -> Self {
371          Self{ state }
372        }
373
374        pub async fn transactional(mut self) -> sqlx::Result<Self> {
375          self.state.db = self.state.db.transaction().await?;
376          Ok(self)
377        }
378
379        pub async fn commit(&self) -> sqlx::Result<()> {
380          self.state.db.commit().await?;
381          Ok(())
382        }
383      }
384
385      #base_section
386
387      #select_section
388
389      #insert_section
390
391      #update_section
392
393      #delete_section
394
395      #(#queries_section)*
396    };
397
398    quoted.into()
399}
400
401fn build_base(conf: &SqlxModelConf) -> TokenStream2 {
402    let state_name = &conf.state_name;
403    let struct_name = &conf.struct_name;
404    let hub_struct = &conf.hub_struct;
405    let attrs_struct = &conf.attrs_struct;
406    let field_idents = &conf.field_idents;
407    let id_type = &conf.id_type;
408    let extra_struct_attributes = &conf.extra_struct_attributes;
409    let hub_builder_method = &conf.hub_builder_method;
410    let select_struct = format_ident!("Select{}Hub", &struct_name);
411    let select_attrs_struct = format_ident!("Select{}", &struct_name);
412    let model_order_by = format_ident!("{}OrderBy", &struct_name);
413    let struct_name_as_string = LitStr::new(&struct_name.to_string(), struct_name.span());
414    let field_types: Vec<Type> = conf.fields.clone().into_iter().map(|i| i.ty).collect();
415
416    let mut belongs_to_structs: Vec<Ident> = vec![];
417    let mut belongs_to_builders: Vec<Ident> = vec![];
418    let mut belongs_to_columns: Vec<Ident> = vec![];
419    let mut maybe_belongs_to_structs: Vec<Ident> = vec![];
420    let mut maybe_belongs_to_builders: Vec<Ident> = vec![];
421    let mut maybe_belongs_to_columns: Vec<Ident> = vec![];
422
423    for c in &conf.belongs_to {
424        let field = conf
425            .fields
426            .iter()
427            .find(|x| *x.ident.as_ref().unwrap() == c.column_name)
428            .unwrap_or_else(|| {
429                panic!(
430                    "Belongs to column {:?} is not a field",
431                    c.column_name.to_string()
432                )
433            });
434
435        let is_option = if let Type::Path(TypePath {
436            path: Path { segments, .. },
437            ..
438        }) = &field.ty
439        {
440            segments[0].ident == "Option"
441        } else {
442            false
443        };
444
445        let builder = Ident::new(
446            &c.model_name.to_string().to_case(Case::Snake),
447            struct_name.span(),
448        );
449
450        if is_option {
451            maybe_belongs_to_structs.push(c.model_name.clone());
452            maybe_belongs_to_columns.push(c.column_name.clone());
453            maybe_belongs_to_builders.push(builder);
454        } else {
455            belongs_to_structs.push(c.model_name.clone());
456            belongs_to_columns.push(c.column_name.clone());
457            belongs_to_builders.push(builder);
458        }
459    }
460
461    let mut has_many_structs: Vec<Ident> = vec![];
462    let mut has_many_builders: Vec<Ident> = vec![];
463    let mut has_many_methods: Vec<Ident> = vec![];
464    let mut has_many_scope_methods: Vec<Ident> = vec![];
465    let mut has_many_select_structs: Vec<Ident> = vec![];
466    let mut has_many_columns: Vec<Ident> = vec![];
467
468    for c in &conf.has_many {
469        let builder = Ident::new(
470            &c.model_name.to_string().to_case(Case::Snake),
471            struct_name.span(),
472        );
473        has_many_methods.push(format_ident!("{}_vec", builder));
474        has_many_scope_methods.push(format_ident!("{}_scope", builder));
475        has_many_select_structs.push(format_ident!("Select{}Hub", c.model_name));
476        has_many_structs.push(c.model_name.clone());
477        has_many_columns.push(format_ident!("{}_eq", c.column_name));
478        has_many_builders.push(builder.clone());
479    }
480
481    let field_attrs: Vec<Vec<Attribute>> = conf
482        .fields
483        .clone()
484        .into_iter()
485        .map(|field| {
486            field
487                .attrs
488                .into_iter()
489                .filter(|a| a.path != parse_str("sqlx_model_hints").unwrap())
490                .collect::<Vec<Attribute>>()
491        })
492        .collect();
493
494    quote! {
495      impl #hub_struct {
496        fn init(&self, attrs: #attrs_struct) -> #struct_name {
497          #struct_name::new(self.state.clone(), attrs)
498        }
499      }
500
501      #[derive(Clone, serde::Serialize)]
502      pub struct #struct_name {
503        #[serde(skip_serializing)]
504        pub state: #state_name,
505        #[serde(flatten)]
506        pub attrs: #attrs_struct,
507      }
508
509      impl #struct_name {
510        pub fn new(state: #state_name, attrs: #attrs_struct) -> Self {
511          Self{ state, attrs }
512        }
513
514        pub async fn reload(&mut self) -> sqlx::Result<()> {
515          self.attrs = self.reloaded().await?.attrs;
516          Ok(())
517        }
518
519        pub async fn reloaded(&self) -> sqlx::Result<Self> {
520          self.state.#hub_builder_method().find(self.id()).await
521        }
522
523        #(
524          pub fn #field_idents<'a>(&'a self) -> &'a #field_types {
525            &self.attrs.#field_idents
526          }
527        )*
528
529        #(
530          pub async fn #belongs_to_builders(&self) -> sqlx::Result<#belongs_to_structs> {
531            self.state.#belongs_to_builders().find(self.#belongs_to_columns()).await
532          }
533        )*
534
535        #(
536          pub async fn #maybe_belongs_to_builders(&self) -> sqlx::Result<Option<#maybe_belongs_to_structs>> {
537            if let Some(a) = self.#maybe_belongs_to_columns() {
538              self.state.#maybe_belongs_to_builders().find(a).await.map(Some)
539            } else {
540              Ok(None)
541            }
542          }
543        )*
544
545        #(
546          pub fn #has_many_scope_methods(&self) -> #has_many_select_structs {
547            self.state.#has_many_builders().select().#has_many_columns(self.id())
548          }
549        )*
550
551        #(
552          pub async fn #has_many_methods(&self) -> sqlx::Result<Vec<#has_many_structs>> {
553            self.#has_many_scope_methods().all().await
554          }
555        )*
556      }
557
558      #[sqlx_models_orm::async_trait]
559      impl sqlx_models_orm::SqlxModel for #struct_name {
560        type State = #state_name;
561        type SelectModelHub = #select_struct;
562        type SelectModel = #select_attrs_struct;
563        type ModelOrderBy = #model_order_by;
564        type ModelHub = #hub_struct;
565        type Id = #id_type;
566      }
567
568      impl PartialEq for #struct_name {
569        fn eq(&self, other: &Self) -> bool {
570          self.attrs == other.attrs
571        }
572      }
573
574      #(#extra_struct_attributes)*
575      #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
576      pub struct #attrs_struct {
577        #(
578          #(#field_attrs)*
579          pub #field_idents: #field_types,
580        )*
581      }
582
583      impl std::fmt::Debug for #struct_name {
584        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585          f.debug_struct(#struct_name_as_string)
586           .field("attrs", &self.attrs)
587           .finish()
588        }
589      }
590    }
591}
592
593fn build_select(conf: &SqlxModelConf) -> TokenStream2 {
594    let state_name = &conf.state_name;
595    let struct_name = &conf.struct_name;
596    let hub_struct = &conf.hub_struct;
597    let table_name = &conf.table_name;
598    let attrs_struct = &conf.attrs_struct;
599    let field_idents = &conf.field_idents;
600    let select_struct = format_ident!("Select{}Hub", &struct_name);
601    let model_order_by = format_ident!("{}OrderBy", &struct_name);
602    let select_attrs_struct = format_ident!("Select{}", &struct_name);
603    let id_type = &conf.id_type;
604    let span = conf.struct_name.span();
605
606    let mut comparison_idents: Vec<Ident> = vec![];
607    let mut comparison_types: Vec<Type> = vec![];
608    let mut builder_method_simple_idents: Vec<Ident> = vec![];
609    let mut builder_method_simple_types: Vec<Type> = vec![];
610    let mut builder_method_string_idents: Vec<Ident> = vec![];
611    let mut where_clauses = vec![];
612    let mut args = vec![];
613
614    let sort_variants: Vec<Ident> = field_idents
615        .iter()
616        .map(|i| Ident::new(&i.to_string().to_case(Case::UpperCamel), i.span()))
617        .collect();
618
619    for field in conf.fields.clone().into_iter() {
620        let ty = field.ty.clone();
621        let flat_ty: syn::Type = if let Type::Path(TypePath {
622            path: Path { segments, .. },
623            ..
624        }) = &ty
625        {
626            if &segments[0].ident.to_string() == "Option" {
627                match &segments[0].arguments {
628                    PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
629                        args,
630                        ..
631                    }) => {
632                        let found = &args[0];
633                        syn::parse_quote! { #found }
634                    }
635                    _ => panic!(
636                        "Type {:?} is too complex. Only simple Option<type> are supported.",
637                        &ty
638                    ),
639                }
640            } else {
641                field.ty.clone()
642            }
643        } else {
644            panic!("Type {:?} expected to be type or Option<type>", &ty);
645        };
646
647        let ident = &field.ident.as_ref().unwrap();
648
649        if let Some(found) = field
650            .attrs
651            .iter()
652            .find(|a| a.path == parse_str("sqlx_model_hints").unwrap())
653        {
654            let hints = found
655                .parse_args::<ModelHints>()
656                .unwrap_or_else(|_| panic!("Arguments for sqlx_model_hints {:?}", found));
657            let db_type = hints.ty.to_string();
658            let mut field_position = args.len();
659
660            let mut comparisons = vec![(format_ident!("{}_eq", ident), "=", &flat_ty, true)];
661
662            if hints.op_ne {
663                comparisons.push((format_ident!("{}_ne", ident), "!=", &flat_ty, true));
664            }
665            if hints.op_gt {
666                comparisons.push((format_ident!("{}_gt", ident), ">", &flat_ty, true));
667            }
668            if hints.op_gte {
669                comparisons.push((format_ident!("{}_gte", ident), ">=", &flat_ty, true));
670            }
671            if hints.op_lt {
672                comparisons.push((format_ident!("{}_lt", ident), "<", &flat_ty, true));
673            }
674            if hints.op_lte {
675                comparisons.push((format_ident!("{}_lte", ident), "<=", &flat_ty, true));
676            }
677
678            let string_ty: syn::Type = syn::parse_quote! { String };
679            if &db_type == "varchar" || &db_type == "text" {
680                if hints.op_like {
681                    comparisons.push((format_ident!("{}_like", ident), "LIKE", &string_ty, false));
682                }
683                if hints.op_not_like {
684                    comparisons.push((
685                        format_ident!("{}_not_like", ident),
686                        "NOT LIKE",
687                        &string_ty,
688                        false,
689                    ));
690                }
691                if hints.op_ilike {
692                    comparisons.push((
693                        format_ident!("{}_ilike", ident),
694                        "ILIKE",
695                        &string_ty,
696                        false,
697                    ));
698                }
699                if hints.op_not_ilike {
700                    comparisons.push((
701                        format_ident!("{}_not_ilike", ident),
702                        "NOT ILIKE",
703                        &string_ty,
704                        false,
705                    ));
706                }
707                if hints.op_similar_to {
708                    comparisons.push((
709                        format_ident!("{}_similar_to", ident),
710                        "SIMILAR TO",
711                        &string_ty,
712                        false,
713                    ));
714                }
715                if hints.op_not_similar_to {
716                    comparisons.push((
717                        format_ident!("{}_not_similar_to", ident),
718                        "NOT SIMILAR TO",
719                        &string_ty,
720                        false,
721                    ));
722                }
723            }
724
725            for (comparison_ident, operator, rust_type, simple_builder) in comparisons.into_iter() {
726                comparison_idents.push(comparison_ident.clone());
727                comparison_types.push(rust_type.clone());
728                where_clauses.push(format!(
729                    "(NOT ${}::boolean OR {} {} ${}::{})",
730                    field_position + 1,
731                    &ident,
732                    operator,
733                    field_position + 2,
734                    &db_type
735                ));
736                field_position += 2;
737
738                args.push(quote! { self.#comparison_ident.is_some() });
739                args.push(quote! { &self.#comparison_ident as &Option<#rust_type> });
740
741                if simple_builder {
742                    builder_method_simple_idents.push(comparison_ident.clone());
743                    builder_method_simple_types.push(rust_type.clone());
744                } else {
745                    builder_method_string_idents.push(comparison_ident.clone());
746                }
747            }
748
749            let vec_of_ty: syn::Type = syn::parse_quote! { Vec<#flat_ty> };
750            let mut field_in_comparisons = vec![];
751            if hints.op_in {
752                field_in_comparisons.push((format_ident!("{}_in", ident), "= ANY", &vec_of_ty));
753            }
754            if hints.op_not_in {
755                field_in_comparisons.push((
756                    format_ident!("{}_not_in", ident),
757                    "<> ALL",
758                    &vec_of_ty,
759                ));
760            }
761
762            for (comparison_ident, operator, rust_type) in field_in_comparisons.into_iter() {
763                comparison_idents.push(comparison_ident.clone());
764                comparison_types.push(rust_type.clone());
765                where_clauses.push(format!(
766                    "(NOT ${}::boolean OR {} {}(CAST(${} as {}[])) )",
767                    field_position + 1,
768                    &ident,
769                    operator,
770                    field_position + 2,
771                    &db_type
772                ));
773                field_position += 2;
774
775                args.push(quote! { self.#comparison_ident.is_some() });
776                args.push(quote! { &self.#comparison_ident as &Option<#rust_type> });
777
778                builder_method_simple_idents.push(comparison_ident.clone());
779                builder_method_simple_types.push(rust_type.clone());
780            }
781
782            if hints.op_is_set {
783                field_position += 1;
784                let is_set_field_ident = format_ident!("{}_is_set", ident);
785                let bool_type: syn::Type = syn::parse_quote! { bool };
786                comparison_idents.push(is_set_field_ident.clone());
787                comparison_types.push(bool_type.clone());
788                where_clauses.push(
789                    format!(
790                      "(${}::boolean IS NULL OR ((${}::boolean AND {} IS NOT NULL) OR (NOT ${}::boolean AND {} IS NULL)))",
791                      field_position,
792                      field_position,
793                      &ident,
794                      field_position,
795                      &ident,
796                    )
797                );
798                args.push(quote! { self.#is_set_field_ident });
799                builder_method_simple_idents.push(is_set_field_ident.clone());
800                builder_method_simple_types.push(bool_type);
801            }
802        };
803    }
804
805    let sort_field_pos = args.len() + 1;
806    let desc_field_pos = args.len() + 2;
807    let limit_field_pos = args.len() + 3;
808    let offset_field_pos = args.len() + 4;
809    args.push(quote! { self.order_by.map(|i| format!("{:?}", i)) as Option<String> });
810    args.push(quote! { self.desc as bool });
811    args.push(quote! { self.limit as Option<i64> });
812    args.push(quote! { self.offset as Option<i64> });
813
814    let mut args_for_count = args.clone();
815    args_for_count.truncate(args.len() - 4);
816
817    let select_struct_str = LitStr::new(&select_struct.to_string(), span);
818
819    let comparison_idents_as_str: Vec<LitStr> = comparison_idents
820        .iter()
821        .map(|i| LitStr::new(&i.to_string(), span))
822        .collect();
823
824    let query_for_find_sort_criteria: String = field_idents
825        .iter()
826        .map(|f| {
827            let variant_name = f.to_string().to_case(Case::UpperCamel);
828            format!(
829                r#"
830      (CASE (${} = '{}' AND NOT ${}) WHEN true THEN {} ELSE NULL END),
831      (CASE (${} = '{}' AND ${}) WHEN true THEN {} ELSE NULL END) DESC
832    "#,
833                sort_field_pos,
834                variant_name,
835                desc_field_pos,
836                f,
837                sort_field_pos,
838                variant_name,
839                desc_field_pos,
840                f
841            )
842        })
843        .collect::<Vec<String>>()
844        .join(",");
845
846    let query_for_find = LitStr::new(
847        &format!(
848            "SELECT {} FROM {} WHERE {} ORDER BY {} LIMIT ${} OFFSET ${}",
849            &conf.sql_select_columns,
850            table_name,
851            where_clauses.join(" AND "),
852            query_for_find_sort_criteria,
853            limit_field_pos,
854            offset_field_pos,
855        ),
856        span,
857    );
858
859    let query_for_find_for_update = LitStr::new(
860        &format!(
861            "SELECT {} FROM {} WHERE {} ORDER BY {} LIMIT ${} OFFSET ${} FOR UPDATE",
862            &conf.sql_select_columns,
863            table_name,
864            where_clauses.join(" AND "),
865            query_for_find_sort_criteria,
866            limit_field_pos,
867            offset_field_pos,
868        ),
869        span,
870    );
871
872    let query_for_count = LitStr::new(
873        &format!(
874            r#"SELECT count(*) as "count!" FROM {} WHERE {}"#,
875            table_name,
876            where_clauses.join(" AND "),
877        ),
878        span,
879    );
880
881    let hub_find_for_update = if conf.no_update {
882        quote! {}
883    } else {
884        quote! {
885          pub async fn find_for_update<T: std::borrow::Borrow<#id_type>>(&self, id: T) -> sqlx::Result<#struct_name> {
886            self.select().id_eq(id.borrow()).one_for_update().await
887          }
888        }
889    };
890
891    let select_for_update_methods = if conf.no_update {
892        quote! {}
893    } else {
894        quote! {
895          pub async fn all_for_update(&self) -> sqlx::Result<Vec<#struct_name>> {
896            let attrs = self.state.db.fetch_all(sqlx::query_as!(#attrs_struct, #query_for_find_for_update, #(#args),*)).await?;
897            Ok(attrs.into_iter().map(|a| self.resource(a) ).collect())
898          }
899
900          pub async fn one_for_update(&self) -> sqlx::Result<#struct_name> {
901            let attrs = self.state.db.fetch_one(sqlx::query_as!(#attrs_struct, #query_for_find_for_update, #(#args),*)).await?;
902            Ok(self.resource(attrs))
903          }
904        }
905    };
906
907    quote! {
908      impl #hub_struct {
909        pub fn select(&self) -> #select_struct {
910          #select_struct::new(self.state.clone())
911        }
912
913        pub async fn find<T: std::borrow::Borrow<#id_type>>(&self, id: T) -> sqlx::Result<#struct_name> {
914          self.select().id_eq(id.borrow()).one().await
915        }
916
917        #hub_find_for_update
918
919        pub async fn find_optional<T: std::borrow::Borrow<#id_type>>(&self, id: T) -> sqlx::Result<Option<#struct_name>> {
920          self.select().id_eq(id.borrow()).optional().await
921        }
922      }
923
924      #[sqlx_models_orm::async_trait]
925      impl sqlx_models_orm::SqlxModelHub<#struct_name> for #hub_struct {
926        fn from_state(state: #state_name) -> Self {
927          #hub_struct::new(state)
928        }
929
930        fn select(&self) -> #select_struct {
931          self.select()
932        }
933
934        async fn find(&self, id: &#id_type) -> sqlx::Result<#struct_name> {
935          self.find(id).await
936        }
937
938        async fn find_optional(&self, id: &#id_type) -> sqlx::Result<Option<#struct_name>> {
939          self.find_optional(id).await
940        }
941      }
942
943      #[derive(sqlx::Type, Debug, Copy, Clone)]
944      #[sqlx(type_name = "varchar", rename_all = "lowercase")]
945      pub enum #model_order_by {
946        #(#sort_variants,)*
947      }
948
949      #[derive(Clone)]
950      pub struct #select_struct {
951        pub state: #state_name,
952        #(pub #comparison_idents: Option<#comparison_types>,)*
953        pub order_by: Option<#model_order_by>,
954        pub desc: bool,
955        pub limit: Option<i64>,
956        pub offset: Option<i64>,
957      }
958
959      impl std::fmt::Debug for #select_struct {
960        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961          f.debug_struct(#select_struct_str)
962           .field("order_by", &self.order_by)
963           .field("desc", &self.desc)
964           .field("limit", &self.limit)
965           .field("offset", &self.offset)
966            #(.field(#comparison_idents_as_str, &self.#comparison_idents))*
967           .finish()
968        }
969      }
970
971      impl #select_struct {
972        pub fn new(state: #state_name) -> Self {
973          Self {
974            state,
975            order_by: None,
976            desc: false,
977            limit: None,
978            offset: None,
979            #(#comparison_idents: None,)*
980          }
981        }
982
983        pub fn order_by(mut self, val: #model_order_by) -> Self {
984          self.order_by = Some(val.clone());
985          self
986        }
987
988        pub fn maybe_order_by(mut self, val: Option<#model_order_by>) -> Self {
989          self.order_by = val.clone();
990          self
991        }
992
993        pub fn desc(mut self, val: bool) -> Self {
994          self.desc = val;
995          self
996        }
997
998        pub fn limit(mut self, val: i64) -> Self {
999          self.limit = Some(val);
1000          self
1001        }
1002
1003        pub fn offset(mut self, val: i64) -> Self {
1004          self.offset = Some(val);
1005          self
1006        }
1007
1008        #(
1009          pub fn #builder_method_simple_idents<T: std::borrow::Borrow<#builder_method_simple_types>>(mut self, val: T) -> Self {
1010            self.#builder_method_simple_idents = Some(val.borrow().to_owned());
1011            self
1012          }
1013        )*
1014
1015        #(
1016          pub fn #builder_method_string_idents<P: AsRef<str>>(mut self, val: P) -> Self
1017          {
1018            self.#builder_method_string_idents = Some(val.as_ref().into());
1019            self
1020          }
1021        )*
1022
1023        pub fn use_struct(mut self, value: #select_attrs_struct) -> Self {
1024          #(self.#comparison_idents = value.#comparison_idents;)*
1025          self.order_by = value.order_by;
1026          self.desc = value.desc;
1027          self.limit = value.limit;
1028          self.offset = value.offset;
1029          self
1030        }
1031
1032        pub async fn all(&self) -> sqlx::Result<Vec<#struct_name>> {
1033          let attrs = self.state.db.fetch_all(sqlx::query_as!(#attrs_struct, #query_for_find, #(#args),*)).await?;
1034          Ok(attrs.into_iter().map(|a| self.resource(a) ).collect())
1035        }
1036
1037        #select_for_update_methods
1038
1039        pub async fn count(&self) -> sqlx::Result<i64> {
1040          self.state.db.fetch_one_scalar(sqlx::query_scalar!(#query_for_count, #(#args_for_count),*)).await
1041        }
1042
1043        pub async fn one(&self) -> sqlx::Result<#struct_name> {
1044          let attrs = self.state.db.fetch_one(sqlx::query_as!(#attrs_struct, #query_for_find, #(#args),*)).await?;
1045          Ok(self.resource(attrs))
1046        }
1047
1048        pub async fn optional(&self) -> sqlx::Result<Option<#struct_name>> {
1049          let attrs = self.state.db.fetch_optional(sqlx::query_as!(#attrs_struct, #query_for_find, #(#args),*)).await?;
1050          Ok(attrs.map(|a| self.resource(a)))
1051        }
1052
1053        fn resource(&self, attrs: #attrs_struct) -> #struct_name {
1054          #struct_name::new(self.state.clone(), attrs)
1055        }
1056      }
1057
1058      #[sqlx_models_orm::async_trait]
1059      impl sqlx_models_orm::SqlxSelectModelHub<#struct_name> for #select_struct {
1060        fn from_state(state: #state_name) -> Self {
1061          #select_struct::new(state)
1062        }
1063
1064        fn order_by(mut self, val: #model_order_by) -> Self {
1065          self.order_by(val)
1066        }
1067
1068        fn maybe_order_by(mut self, val: Option<#model_order_by>) -> Self {
1069          self.maybe_order_by(val)
1070        }
1071
1072        fn desc(self, val: bool) -> Self {
1073          self.desc(val)
1074        }
1075
1076        fn limit(self, val: i64) -> Self {
1077          self.limit(val)
1078        }
1079
1080        fn offset(self, val: i64) -> Self {
1081          self.offset(val)
1082        }
1083
1084        fn use_struct(self, value: #select_attrs_struct) -> Self {
1085          self.use_struct(value)
1086        }
1087
1088        async fn all(&self) -> sqlx::Result<Vec<#struct_name>> {
1089          self.all().await
1090        }
1091
1092        async fn count(&self) -> sqlx::Result<i64> {
1093          self.count().await
1094        }
1095
1096        async fn one(&self) -> sqlx::Result<#struct_name> {
1097          self.one().await
1098        }
1099
1100        async fn optional(&self) -> sqlx::Result<Option<#struct_name>> {
1101          self.optional().await
1102        }
1103      }
1104
1105      #[derive(Debug, Default)]
1106      pub struct #select_attrs_struct {
1107        #(pub #comparison_idents: Option<#comparison_types>,)*
1108        pub order_by: Option<#model_order_by>,
1109        pub desc: bool,
1110        pub limit: Option<i64>,
1111        pub offset: Option<i64>,
1112      }
1113    }
1114}
1115
1116fn build_queries(conf: &SqlxModelConf) -> Vec<TokenStream2> {
1117    let state_name = &conf.state_name;
1118    let struct_name = &conf.struct_name;
1119    let hub_struct = &conf.hub_struct;
1120    let table_name = &conf.table_name;
1121    let attrs_struct = &conf.attrs_struct;
1122    let span = conf.struct_name.span();
1123
1124    conf.queries.iter().map(|q|{
1125    let method_name = q.method_name.clone();
1126    let sql = q.sql.clone();
1127    let args = q.args.clone();
1128    let arg_names: Vec<Ident> = q.args.iter().map(|i| i.name.clone().unwrap().0 ).collect();
1129    let arg_types: Vec<Type> = q.args.iter().map(|i| i.ty.clone() ).collect();
1130    let query_struct_name = Ident::new(&method_name.to_string().to_case(Case::UpperCamel), q.method_name.span());
1131
1132    let query = LitStr::new(&format!(
1133      "SELECT {} FROM {} WHERE {}",
1134      &conf.sql_select_columns,
1135      table_name,
1136      sql.value()
1137    ), span);
1138
1139    let query_for_count = LitStr::new(&format!(
1140      r#"SELECT count(*) as "count!" FROM (SELECT 1 FROM {} WHERE {})"#,
1141      table_name,
1142      sql.value()
1143    ), span);
1144
1145    quote!{
1146      pub struct #query_struct_name {
1147        state: #state_name,
1148        #args
1149      }
1150
1151      impl #query_struct_name {
1152        fn init(&self, attrs: #attrs_struct) -> #struct_name {
1153          #struct_name::new(self.state.clone(), attrs)
1154        }
1155
1156        pub async fn all(&self) -> sqlx::Result<Vec<#struct_name>> {
1157          let attrs = self.state.db.fetch_all(sqlx::query_as!(#attrs_struct, #query, #(&self.#arg_names as &#arg_types),*)).await?;
1158          Ok(attrs.into_iter().map(|a| self.init(a) ).collect())
1159        }
1160
1161        pub async fn one(&self) -> sqlx::Result<#struct_name> {
1162          let attrs = self.state.db.fetch_one(sqlx::query_as!(#attrs_struct, #query, #(&self.#arg_names as &#arg_types),*)).await?;
1163          Ok(self.init(attrs))
1164        }
1165
1166        pub async fn optional(&self) -> sqlx::Result<Option<#struct_name>> {
1167          let attrs = self.state.db.fetch_optional(sqlx::query_as!(#attrs_struct, #query, #(&self.#arg_names as &#arg_types),*)).await?;
1168          Ok(attrs.map(|a| self.init(a)))
1169        }
1170
1171        pub async fn count(&self) -> sqlx::Result<i64> {
1172          self.state.db.fetch_one_scalar(sqlx::query_scalar!(#query_for_count, #(&self.#arg_names as &#arg_types),*)).await
1173        }
1174      }
1175
1176      impl #hub_struct {
1177        #[allow(clippy::too_many_arguments)]
1178        pub fn #method_name(&self, #args) -> #query_struct_name {
1179          #query_struct_name{ state: self.state.clone(), #(#arg_names,)* }
1180        }
1181      }
1182    }
1183  }).collect()
1184}
1185
1186fn build_insert(conf: &SqlxModelConf) -> TokenStream2 {
1187    let span = conf.struct_name.span();
1188    let state_name = &conf.state_name;
1189    let struct_name = &conf.struct_name;
1190    let hub_struct = &conf.hub_struct;
1191    let table_name = &conf.table_name;
1192    let attrs_struct = &conf.attrs_struct;
1193    let extra_struct_attributes = &conf.extra_struct_attributes;
1194
1195    let fields_for_insert: Vec<Field> = conf
1196        .fields
1197        .clone()
1198        .into_iter()
1199        .filter(|field| {
1200            match field
1201                .attrs
1202                .iter()
1203                .find(|a| a.path == parse_str("sqlx_model_hints").unwrap())
1204            {
1205                None => true,
1206                Some(found) => {
1207                    let hint: ModelHints = found.parse_args().unwrap();
1208                    !hint.default
1209                }
1210            }
1211        })
1212        .collect();
1213
1214    let fields_for_insert_idents: Vec<Ident> = fields_for_insert
1215        .iter()
1216        .map(|i| i.ident.as_ref().unwrap().clone())
1217        .collect();
1218    let fields_for_insert_types: Vec<Type> =
1219        fields_for_insert.iter().map(|i| i.ty.clone()).collect();
1220
1221    let fields_for_insert_as_string: Vec<LitStr> = fields_for_insert_idents
1222        .iter()
1223        .map(|i| LitStr::new(&i.to_string(), i.span()))
1224        .collect();
1225
1226    let fields_for_insert_attrs: Vec<Vec<Attribute>> = fields_for_insert
1227        .clone()
1228        .into_iter()
1229        .map(|field| {
1230            field
1231                .attrs
1232                .into_iter()
1233                .filter(|a| a.path != parse_str("sqlx_model_hints").unwrap())
1234                .collect::<Vec<Attribute>>()
1235        })
1236        .collect();
1237
1238    let insert_struct = format_ident!("Insert{}Hub", &struct_name);
1239    let insert_struct_as_string = LitStr::new(&insert_struct.to_string(), span);
1240    let insert_attrs_struct = format_ident!("Insert{}", &struct_name);
1241
1242    let column_names_to_insert = fields_for_insert_idents
1243        .iter()
1244        .map(|f| f.to_string())
1245        .collect::<Vec<String>>()
1246        .join(", \n");
1247
1248    let column_names_to_insert_positions = fields_for_insert
1249        .iter()
1250        .enumerate()
1251        .map(|(n, _)| format!("${}", n + 1))
1252        .collect::<Vec<String>>()
1253        .join(", ");
1254
1255    let query_for_insert = LitStr::new(
1256        &format!(
1257            "INSERT INTO {} ({}) VALUES ({}) RETURNING {}",
1258            table_name,
1259            column_names_to_insert,
1260            column_names_to_insert_positions,
1261            &conf.sql_select_columns,
1262        ),
1263        span,
1264    );
1265
1266    let query_for_insert_no_conflict = LitStr::new(
1267        &format!(
1268    "INSERT INTO {} ({}) VALUES ({}) ON CONFLICT (id) DO UPDATE SET id = {}.id RETURNING {}",
1269    table_name,
1270    column_names_to_insert,
1271    column_names_to_insert_positions,
1272    table_name,
1273    &conf.sql_select_columns,
1274  ),
1275        span,
1276    );
1277
1278    quote! {
1279      impl #hub_struct {
1280        #[must_use = "don't forget to save your insert"]
1281        pub fn insert(&self, attrs: #insert_attrs_struct) -> #insert_struct {
1282          #insert_struct::new(self.state.clone(), attrs)
1283        }
1284      }
1285
1286      #[derive(Clone)]
1287      #[must_use="don't forget to save() your insert"]
1288      pub struct #insert_struct {
1289        pub state: #state_name,
1290        pub attrs: #insert_attrs_struct,
1291      }
1292
1293      impl #insert_struct {
1294        pub fn new(
1295          state: #state_name,
1296          attrs: #insert_attrs_struct
1297        ) -> Self {
1298          Self{ state, attrs }
1299        }
1300
1301        #(
1302          pub fn #fields_for_insert_idents(&self) -> &#fields_for_insert_types {
1303            &self.attrs.#fields_for_insert_idents
1304          }
1305        )*
1306
1307        pub fn use_struct(mut self, attrs: #insert_attrs_struct) -> Self {
1308          self.attrs = attrs;
1309          self
1310        }
1311
1312        pub async fn save(self) -> std::result::Result<#struct_name, sqlx::Error> {
1313          let attrs = self.state.db.fetch_one(
1314            sqlx::query_as!(
1315              #attrs_struct,
1316              #query_for_insert,
1317              #(&self.attrs.#fields_for_insert_idents as &#fields_for_insert_types),*
1318            )
1319          ).await?;
1320
1321          Ok(#struct_name::new(self.state.clone(), attrs))
1322        }
1323
1324        pub async fn save_no_conflict(self) -> std::result::Result<#struct_name, sqlx::Error> {
1325          let attrs = self.state.db.fetch_one(
1326            sqlx::query_as!(
1327              #attrs_struct,
1328              #query_for_insert_no_conflict,
1329              #(&self.attrs.#fields_for_insert_idents as &#fields_for_insert_types),*
1330            )
1331          ).await?;
1332
1333          Ok(#struct_name::new(self.state.clone(), attrs))
1334        }
1335      }
1336
1337      impl std::fmt::Debug for #insert_struct {
1338        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1339          f.debug_struct(#insert_struct_as_string)
1340            #(
1341              .field(#fields_for_insert_as_string, &self.attrs.#fields_for_insert_idents)
1342            )*
1343           .finish()
1344        }
1345      }
1346
1347      #(#extra_struct_attributes)*
1348      #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1349      pub struct #insert_attrs_struct {
1350        #(
1351          #(#fields_for_insert_attrs)*
1352          pub #fields_for_insert_idents: #fields_for_insert_types,
1353        )*
1354      }
1355    }
1356}
1357
1358fn build_update(conf: &SqlxModelConf) -> TokenStream2 {
1359    let span = conf.struct_name.span();
1360    let state_name = &conf.state_name;
1361    let struct_name = &conf.struct_name;
1362    let table_name = &conf.table_name;
1363    let attrs_struct = &conf.attrs_struct;
1364    let fields = &conf.fields;
1365    let field_idents = &conf.field_idents;
1366    let id_type = &conf.id_type;
1367    let field_types: Vec<Type> = fields.clone().into_iter().map(|i| i.ty).collect();
1368
1369    let update_struct = format_ident!("Update{}Hub", &struct_name);
1370    let update_attrs_struct = format_ident!("Update{}", &struct_name);
1371
1372    let mut args_for_update = vec![];
1373
1374    for field in fields.clone().into_iter() {
1375        let ty = field.ty;
1376        let ident = field.ident.unwrap();
1377        if let Type::Path(TypePath {
1378            path: Path { ref segments, .. },
1379            ..
1380        }) = ty
1381        {
1382            args_for_update.push(quote! { &self.attrs.#ident.is_some() as &bool });
1383            if &segments[0].ident.to_string() == "Option" {
1384                args_for_update.push(quote! { &self.attrs.#ident.clone().flatten() as &#ty });
1385            } else {
1386                args_for_update.push(quote! { &self.attrs.#ident as &Option<#ty> });
1387            };
1388        }
1389    }
1390
1391    let column_names_to_insert = field_idents
1392        .iter()
1393        .map(|f| f.to_string())
1394        .collect::<Vec<String>>()
1395        .join(", \n");
1396
1397    let column_names_to_update_positions = field_idents
1398        .iter()
1399        .enumerate()
1400        .map(|(n, f)| {
1401            let base_pos = 2 + (n * 2);
1402            format!(
1403                "(CASE ${}::boolean WHEN TRUE THEN ${} ELSE {} END)",
1404                base_pos,
1405                base_pos + 1,
1406                f.clone()
1407            )
1408        })
1409        .collect::<Vec<String>>()
1410        .join(", ");
1411
1412    let query_for_update = LitStr::new(
1413        &format!(
1414            "UPDATE {} SET ({}) = ({}) WHERE id = $1 RETURNING {}",
1415            table_name,
1416            column_names_to_insert,
1417            column_names_to_update_positions,
1418            &conf.sql_select_columns,
1419        ),
1420        span,
1421    );
1422
1423    quote! {
1424      impl #struct_name {
1425        #[must_use = "don't forget to save your update"]
1426        pub fn update(self) -> #update_struct {
1427          #update_struct::new(self.state, self.attrs.id)
1428        }
1429      }
1430
1431      #[must_use = "don't forget to save your update"]
1432      pub struct #update_struct {
1433        pub state: #state_name,
1434        pub attrs: #update_attrs_struct,
1435        pub id: #id_type,
1436      }
1437
1438      impl #update_struct {
1439        pub fn new(state: #state_name, id: #id_type) -> Self {
1440          Self{ state, id, attrs: Default::default() }
1441        }
1442
1443        #(
1444          pub fn #field_idents(mut self, val: #field_types) -> Self {
1445            self.attrs.#field_idents = Some(val);
1446            self
1447          }
1448        )*
1449
1450        pub fn use_struct(mut self, value: #update_attrs_struct) -> Self {
1451          self.attrs = value;
1452          self
1453        }
1454
1455        pub async fn save(self) -> std::result::Result<#struct_name, sqlx::Error> {
1456          let attrs = self.state.db.fetch_one(
1457            sqlx::query_as!(
1458              #attrs_struct,
1459              #query_for_update,
1460              self.id,
1461              #(#args_for_update),*
1462            )
1463          ).await?;
1464
1465          Ok(#struct_name::new(self.state.clone(), attrs))
1466        }
1467      }
1468
1469      #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1470      pub struct #update_attrs_struct {
1471        #( pub #field_idents: Option<#field_types>,)*
1472      }
1473    }
1474}
1475
1476fn build_delete(conf: &SqlxModelConf) -> TokenStream2 {
1477    let struct_name = &conf.struct_name;
1478    let table_name = &conf.table_name;
1479    let span = conf.struct_name.span();
1480
1481    let query_for_delete = LitStr::new(&format!("DELETE FROM {} WHERE id = $1", table_name), span);
1482
1483    quote! {
1484      impl #struct_name {
1485        pub async fn delete(self) -> sqlx::Result<()> {
1486          self.state.db.execute(sqlx::query!(#query_for_delete, self.attrs.id)).await?;
1487          Ok(())
1488        }
1489      }
1490    }
1491}