aykroyd_derive/
lib.rs

1use quote::quote;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4enum Key {
5    Index,
6    Name,
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10enum Delegate {
11    FromColumn,
12    FromColumns,
13}
14
15/// Derive macro available if aykroyd is built with `features = ["derive"]`.
16#[proc_macro_derive(Statement, attributes(aykroyd))]
17pub fn derive_statement(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
18    let ast: syn::DeriveInput = syn::parse(input).unwrap();
19
20    let name = &ast.ident;
21    let generics = &ast.generics;
22
23    let fields = match &ast.data {
24        syn::Data::Enum(_) => panic!("Cannot derive Statement on enum!"),
25        syn::Data::Union(_) => panic!("Cannot derive Statement on union!"),
26        syn::Data::Struct(s) => &s.fields,
27    };
28    let fields = match fields {
29        syn::Fields::Unit => vec![],
30        syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
31        | syn::Fields::Unnamed(syn::FieldsUnnamed {
32            unnamed: fields, ..
33        }) => fields.iter().collect(),
34    };
35    let fields = ParamInfo::from_fields(&fields);
36
37    let info = StatementInfo::from_attrs(&ast.attrs);
38
39    let query_text_impl = impl_static_query_text(name, generics, &info.query_text);
40    let to_params_impl = impl_to_params(name, generics, &fields);
41    let statement_impl = impl_statement(name, generics);
42
43    let body = quote!(#query_text_impl #to_params_impl #statement_impl);
44    body.into()
45}
46
47struct StatementInfo {
48    query_text: String,
49}
50
51impl StatementInfo {
52    fn from_attrs(attrs: &[syn::Attribute]) -> StatementInfo {
53        let attr = attrs
54            .iter()
55            .find(|attr| attr.path().is_ident("aykroyd"))
56            .unwrap();
57
58        let mut text = None;
59        let mut file = None;
60
61        attr.parse_nested_meta(|meta| {
62            if meta.path.is_ident("text") {
63                let value = meta.value()?;
64                let source: syn::LitStr = value.parse()?;
65                text = Some(source.value());
66                return Ok(());
67            }
68
69            if meta.path.is_ident("file") {
70                let value = meta.value()?;
71                let filename: syn::LitStr = value.parse()?;
72                let path = std::path::PathBuf::from("queries").join(filename.value());
73                let source = std::fs::read_to_string(path).unwrap();
74                file = Some(source);
75                return Ok(());
76            }
77
78            Err(meta.error("unknown meta path"))
79        })
80        .unwrap();
81
82        let query_text = match (text, file) {
83            (Some(_), Some(_)) => panic!("use one of file or text"),
84            (Some(q), None) => q,
85            (None, Some(q)) => q,
86            (None, None) => panic!("unable to find query text"),
87        };
88
89        StatementInfo { query_text }
90    }
91}
92
93/// Derive macro available if aykroyd is built with `features = ["derive"]`.
94#[proc_macro_derive(Query, attributes(aykroyd))]
95pub fn derive_query(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
96    let ast: syn::DeriveInput = syn::parse(input).unwrap();
97
98    let name = &ast.ident;
99    let generics = &ast.generics;
100
101    let fields = match &ast.data {
102        syn::Data::Enum(_) => panic!("Cannot derive Query on enum!"),
103        syn::Data::Union(_) => panic!("Cannot derive Query on union!"),
104        syn::Data::Struct(s) => &s.fields,
105    };
106    let fields = match fields {
107        syn::Fields::Unit => vec![],
108        syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
109        | syn::Fields::Unnamed(syn::FieldsUnnamed {
110            unnamed: fields, ..
111        }) => fields.iter().collect(),
112    };
113    let fields = ParamInfo::from_fields(&fields);
114
115    let info = QueryInfo::from_attrs(&ast.attrs);
116
117    let query_text_impl = impl_static_query_text(name, generics, &info.query_text);
118    let to_params_impl = impl_to_params(name, generics, &fields);
119    let query_impl = impl_query(name, generics, &info.row);
120
121    let body = quote!(#query_text_impl #to_params_impl #query_impl);
122    body.into()
123}
124
125/// Derive macro available if aykroyd is built with `features = ["derive"]`.
126#[proc_macro_derive(QueryOne, attributes(aykroyd))]
127pub fn derive_query_one(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
128    let ast: syn::DeriveInput = syn::parse(input).unwrap();
129
130    let name = &ast.ident;
131    let generics = &ast.generics;
132
133    let fields = match &ast.data {
134        syn::Data::Enum(_) => panic!("Cannot derive QueryOne on enum!"),
135        syn::Data::Union(_) => panic!("Cannot derive QueryOne on union!"),
136        syn::Data::Struct(s) => &s.fields,
137    };
138    let fields = match fields {
139        syn::Fields::Unit => vec![],
140        syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
141        | syn::Fields::Unnamed(syn::FieldsUnnamed {
142            unnamed: fields, ..
143        }) => fields.iter().collect(),
144    };
145    let fields = ParamInfo::from_fields(&fields);
146
147    let info = QueryInfo::from_attrs(&ast.attrs);
148
149    let query_text_impl = impl_static_query_text(name, generics, &info.query_text);
150    let to_params_impl = impl_to_params(name, generics, &fields);
151    let query_impl = impl_query(name, generics, &info.row);
152    let query_one_impl = impl_query_one(name, generics);
153
154    let body = quote!(#query_text_impl #to_params_impl #query_impl #query_one_impl);
155    body.into()
156}
157
158struct QueryInfo {
159    query_text: String,
160    row: syn::Type,
161}
162
163impl QueryInfo {
164    fn from_attrs(attrs: &[syn::Attribute]) -> QueryInfo {
165        let attr = attrs
166            .iter()
167            .find(|attr| attr.path().is_ident("aykroyd"))
168            .unwrap();
169
170        let mut text = None;
171        let mut file = None;
172        let mut row = None;
173
174        attr.parse_nested_meta(|meta| {
175            if meta.path.is_ident("text") {
176                let value = meta.value()?;
177                let source: syn::LitStr = value.parse()?;
178                text = Some(source.value());
179                return Ok(());
180            }
181
182            if meta.path.is_ident("file") {
183                let value = meta.value()?;
184                let filename: syn::LitStr = value.parse()?;
185                let path = std::path::PathBuf::from("queries").join(filename.value());
186                let source = std::fs::read_to_string(path).unwrap();
187                file = Some(source);
188                return Ok(());
189            }
190
191            if meta.path.is_ident("row") {
192                let content;
193                syn::parenthesized!(content in meta.input);
194                let ty: syn::Type = content.parse()?;
195                row = Some(ty);
196                return Ok(());
197            }
198
199            Err(meta.error("unknown meta path"))
200        })
201        .unwrap();
202
203        let query_text = match (text, file) {
204            (Some(_), Some(_)) => panic!("use one of file or text"),
205            (Some(q), None) => q,
206            (None, Some(q)) => q,
207            (None, None) => panic!("unable to find query text"),
208        };
209
210        let row = match row {
211            Some(r) => r,
212            None => panic!("unable to find row type"),
213        };
214
215        QueryInfo { query_text, row }
216    }
217}
218
219struct ParamInfo {
220    ident: Option<syn::Ident>,
221    ty: syn::Type,
222    param: Option<usize>,
223}
224
225impl ParamInfo {
226    fn from_fields(fields: &[&syn::Field]) -> Vec<ParamInfo> {
227        fields
228            .iter()
229            .map(|field| {
230                let ident = field.ident.clone();
231                let ty = field.ty.clone();
232                let mut param = None;
233
234                for attr in &field.attrs {
235                    if attr.path().is_ident("aykroyd") {
236                        attr.parse_nested_meta(|meta| {
237                            if meta.path.is_ident("param") {
238                                let value = meta.value()?;
239                                let inner = value.parse()?;
240
241                                let inner = match inner {
242                                    syn::Lit::Int(n) => {
243                                        let value: usize = n
244                                            .base10_parse()
245                                            .map_err(|e| meta.error(e.to_string()))?;
246                                        value
247                                    }
248                                    syn::Lit::Str(s) => {
249                                        let text = s.value();
250                                        let text = text.strip_prefix('$').unwrap_or(&text);
251                                        let value: usize = text
252                                            .parse()
253                                            .map_err(|_| meta.error("invalid param"))?;
254                                        value
255                                    }
256                                    _ => return Err(meta.error("invalid param")),
257                                };
258
259                                param = Some(inner);
260                                return Ok(());
261                            }
262
263                            Err(meta.error("unrecognized attr"))
264                        })
265                        .unwrap();
266                    }
267                }
268
269                ParamInfo { ident, ty, param }
270            })
271            .collect()
272    }
273}
274
275fn simplify(generics: &syn::Generics) -> proc_macro2::TokenStream {
276    let params = generics.params.iter().map(|param| {
277        use syn::GenericParam::*;
278        match param {
279            Lifetime(syn::LifetimeParam { lifetime, .. }) => quote!(#lifetime),
280            Type(syn::TypeParam { ident, .. }) => quote!(#ident),
281            Const(syn::ConstParam { ident, .. }) => quote!(#ident),
282        }
283    });
284
285    quote!(<#(#params)*>)
286}
287
288fn insert_c(generics: &syn::Generics) -> syn::Generics {
289    let param = syn::TypeParam {
290        attrs: vec![],
291        ident: syn::Ident::new("C", proc_macro2::Span::call_site()),
292        colon_token: None,
293        bounds: syn::punctuated::Punctuated::new(),
294        eq_token: None,
295        default: None,
296    };
297
298    let mut generics = generics.clone();
299    generics.params.insert(0, syn::GenericParam::Type(param));
300    generics
301}
302
303fn impl_static_query_text(
304    name: &syn::Ident,
305    generics: &syn::Generics,
306    query_text: &str,
307) -> proc_macro2::TokenStream {
308    let generics_simple = simplify(generics);
309    let query_text = query_text.trim();
310    quote! {
311        #[automatically_derived]
312        impl #generics ::aykroyd::query::StaticQueryText for #name #generics_simple {
313            const QUERY_TEXT: &'static ::std::primitive::str = #query_text;
314        }
315    }
316}
317
318fn impl_to_params(
319    name: &syn::Ident,
320    generics: &syn::Generics,
321    fields: &[ParamInfo],
322) -> proc_macro2::TokenStream {
323    let mut params = vec![];
324    let mut wheres = vec![];
325
326    let mut has_index = std::collections::HashMap::new();
327    let mut no_index = std::collections::VecDeque::new();
328
329    for field in fields {
330        match &field.param {
331            Some(param) => {
332                has_index.insert(param, field);
333            }
334            None => {
335                no_index.push_front(field);
336            }
337        }
338    }
339
340    for index in 0..fields.len() {
341        let param = index + 1;
342        let field = if has_index.contains_key(&param) {
343            has_index.remove(&param).expect("index")
344        } else {
345            no_index.pop_back().expect("noindex")
346        };
347
348        let name = match &field.ident {
349            Some(name) => quote!(#name),
350            None => {
351                let index = index as u32;
352                let span = proc_macro2::Span::call_site();
353                let index = syn::Index { index, span };
354                quote!(#index)
355            }
356        };
357        params.push(quote! {
358            ::aykroyd::client::ToParam::to_param(&self.#name)
359        });
360
361        let ty = &field.ty;
362        wheres.push(quote! {
363            #ty: ::aykroyd::client::ToParam<C>
364        });
365    }
366
367    let body = if params.is_empty() {
368        quote!(::std::option::Option::None)
369    } else {
370        quote!(::std::option::Option::Some(::std::vec![#(#params,)*]))
371    };
372
373    let generics_simple = simplify(generics);
374    let generics = insert_c(generics);
375    quote! {
376        #[automatically_derived]
377        impl #generics ::aykroyd::query::ToParams<C> for #name #generics_simple
378        where
379            C: ::aykroyd::client::Client,
380            #(#wheres,)*
381        {
382            fn to_params(
383                &self
384            ) -> ::std::option::Option<
385                ::std::vec::Vec<
386                    <C as ::aykroyd::client::Client>::Param<'_>
387                >
388            > {
389                #body
390            }
391        }
392    }
393}
394
395fn impl_statement(name: &syn::Ident, generics: &syn::Generics) -> proc_macro2::TokenStream {
396    let generics_simple = simplify(generics);
397    let generics = insert_c(generics);
398    quote! {
399        #[automatically_derived]
400        impl #generics ::aykroyd::Statement<C> for #name #generics_simple
401        where
402            C: ::aykroyd::client::Client,
403            Self: ::aykroyd::query::ToParams<C>,
404        {
405        }
406    }
407}
408
409fn impl_query(
410    name: &syn::Ident,
411    generics: &syn::Generics,
412    row: &syn::Type,
413) -> proc_macro2::TokenStream {
414    let generics_simple = simplify(generics);
415    let generics = insert_c(generics);
416    quote! {
417        #[automatically_derived]
418        impl #generics ::aykroyd::Query<C> for #name #generics_simple
419        where
420            C: ::aykroyd::client::Client,
421            #row: ::aykroyd::FromRow<C>,
422            Self: ::aykroyd::query::ToParams<C>,
423        {
424            type Row = #row;
425        }
426    }
427}
428
429fn impl_query_one(name: &syn::Ident, generics: &syn::Generics) -> proc_macro2::TokenStream {
430    let generics_simple = simplify(generics);
431    let generics = insert_c(generics);
432    quote! {
433        #[automatically_derived]
434        impl #generics ::aykroyd::QueryOne<C> for #name #generics_simple
435        where
436            C: ::aykroyd::client::Client,
437            Self: ::aykroyd::Query<C>,
438        {
439        }
440    }
441}
442
443/// Derive macro available if aykroyd is built with `features = ["derive"]`.
444#[proc_macro_derive(FromRow, attributes(aykroyd))]
445pub fn derive_from_row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
446    let ast: syn::DeriveInput = syn::parse(input).unwrap();
447
448    let name = &ast.ident;
449    let fields = match &ast.data {
450        syn::Data::Enum(_) => panic!("Cannot derive FromRow on enum!"),
451        syn::Data::Union(_) => panic!("Cannot derive FromRow on union!"),
452        syn::Data::Struct(s) => &s.fields,
453    };
454    let tuple_struct = match fields {
455        syn::Fields::Unit | syn::Fields::Unnamed(_) => true,
456        syn::Fields::Named(_) => false,
457    };
458    let fields = match fields {
459        syn::Fields::Unit => vec![],
460        syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
461        | syn::Fields::Unnamed(syn::FieldsUnnamed {
462            unnamed: fields, ..
463        }) => fields.iter().collect(),
464    };
465    let fields = FieldInfo::from_fields(&fields);
466
467    let mut key = None;
468
469    if let Some(attr) = ast
470        .attrs
471        .iter()
472        .find(|attr| attr.path().is_ident("aykroyd"))
473    {
474        attr.parse_nested_meta(|meta| {
475            if meta.path.is_ident("by_index") {
476                key = Some(Key::Index);
477                return Ok(());
478            }
479
480            if meta.path.is_ident("by_name") {
481                key = Some(Key::Name);
482                return Ok(());
483            }
484
485            Err(meta.error("unknown meta path"))
486        })
487        .unwrap();
488    }
489
490    let key = match FieldInfo::key_for(key, &fields) {
491        Err(message) => return message.into(),
492        Ok(key) => key,
493    };
494    let key = key.unwrap_or(if tuple_struct { Key::Index } else { Key::Name });
495
496    let from_columns_impl = impl_from_columns(key, name, tuple_struct, &fields[..]);
497    let from_row_impl = impl_from_row(key, name);
498
499    let body = quote!(#from_row_impl #from_columns_impl);
500    body.into()
501}
502
503/// Derive macro available if aykroyd is built with `features = ["derive"]`.
504#[proc_macro_derive(FromColumnsIndexed, attributes(aykroyd))]
505pub fn derive_from_columns_indexed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
506    let ast: syn::DeriveInput = syn::parse(input).unwrap();
507
508    let name = &ast.ident;
509    let fields = match &ast.data {
510        syn::Data::Enum(_) => panic!("Cannot derive FromColumnsIndexed on enum!"),
511        syn::Data::Union(_) => panic!("Cannot derive FromColumnsIndexed on union!"),
512        syn::Data::Struct(s) => &s.fields,
513    };
514    let tuple_struct = match fields {
515        syn::Fields::Unit | syn::Fields::Unnamed(_) => true,
516        syn::Fields::Named(_) => false,
517    };
518    let fields = match fields {
519        syn::Fields::Unit => vec![],
520        syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
521        | syn::Fields::Unnamed(syn::FieldsUnnamed {
522            unnamed: fields, ..
523        }) => fields.iter().collect(),
524    };
525    let fields = FieldInfo::from_fields(&fields);
526    if let Err(message) = FieldInfo::assert_key(Key::Index, &fields) {
527        return message.into();
528    }
529
530    let body = impl_from_columns(Key::Index, name, tuple_struct, &fields[..]);
531    body.into()
532}
533
534/// Derive macro available if aykroyd is built with `features = ["derive"]`.
535#[proc_macro_derive(FromColumnsNamed, attributes(aykroyd))]
536pub fn derive_from_columns_named(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
537    let ast: syn::DeriveInput = syn::parse(input).unwrap();
538
539    let name = &ast.ident;
540    let fields = match &ast.data {
541        syn::Data::Enum(_) => panic!("Cannot derive FromColumnsNamed on enum!"),
542        syn::Data::Union(_) => panic!("Cannot derive FromColumnsNamed on union!"),
543        syn::Data::Struct(s) => &s.fields,
544    };
545    let tuple_struct = match fields {
546        syn::Fields::Unit | syn::Fields::Unnamed(_) => true,
547        syn::Fields::Named(_) => false,
548    };
549    let fields = match fields {
550        syn::Fields::Unit => vec![],
551        syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
552        | syn::Fields::Unnamed(syn::FieldsUnnamed {
553            unnamed: fields, ..
554        }) => fields.iter().collect(),
555    };
556    let fields = FieldInfo::from_fields(&fields);
557    if let Err(message) = FieldInfo::assert_key(Key::Index, &fields) {
558        return message.into();
559    }
560
561    let body = impl_from_columns(Key::Name, name, tuple_struct, &fields[..]);
562    body.into()
563}
564
565struct FieldInfo {
566    ident: Option<syn::Ident>,
567    ty: syn::Type,
568    nested: bool,
569    column: Option<syn::Lit>,
570}
571
572impl FieldInfo {
573    fn from_fields(fields: &[&syn::Field]) -> Vec<FieldInfo> {
574        fields
575            .iter()
576            .map(|field| {
577                let ident = field.ident.clone();
578                let ty = field.ty.clone();
579                let mut nested = false;
580                let mut column = None;
581
582                for attr in &field.attrs {
583                    if attr.path().is_ident("aykroyd") {
584                        attr.parse_nested_meta(|meta| {
585                            if meta.path.is_ident("nested") {
586                                nested = true;
587                                return Ok(());
588                            }
589
590                            if meta.path.is_ident("column") {
591                                let value = meta.value()?;
592                                let inner = value.parse()?;
593                                column = Some(inner);
594                                return Ok(());
595                            }
596
597                            Err(meta.error("unrecognized attr"))
598                        })
599                        .unwrap();
600                    }
601                }
602
603                FieldInfo {
604                    ident,
605                    ty,
606                    nested,
607                    column,
608                }
609            })
610            .collect()
611    }
612
613    fn assert_key(
614        expected: Key,
615        fields: &[FieldInfo],
616    ) -> Result<Option<Key>, proc_macro2::TokenStream> {
617        FieldInfo::key_for(Some(expected), fields)
618    }
619
620    fn key_for(
621        expected: Option<Key>,
622        fields: &[FieldInfo],
623    ) -> Result<Option<Key>, proc_macro2::TokenStream> {
624        let key = fields
625            .iter()
626            .find_map(|field| field.column.as_ref())
627            .map(|lit| match lit {
628                syn::Lit::Int(_) => Ok(Key::Index),
629                syn::Lit::Str(_) => Ok(Key::Name),
630                _ => Err(quote::quote_spanned! {
631                    lit.span() => ::std::compile_error!("invalid column key");
632                }),
633            })
634            .transpose()?;
635
636        if let Some(key) = key {
637            let key = expected.unwrap_or(key);
638            for field in fields {
639                match key {
640                    Key::Index => match &field.column {
641                        Some(syn::Lit::Int(_)) => {}
642                        Some(lit) => {
643                            return Err(quote::quote_spanned! {
644                                lit.span() => ::std::compile_error!("expected column index");
645                            });
646                        }
647                        None => {
648                            use syn::spanned::Spanned;
649                            return Err(quote::quote_spanned! {
650                                field.ty.span() => ::std::compile_error!("expected column index");
651                            });
652                        }
653                    },
654                    Key::Name => {
655                        match &field.column {
656                            Some(syn::Lit::Str(_)) => {}
657                            Some(lit) => {
658                                return Err(quote::quote_spanned! {
659                                    lit.span() => ::std::compile_error!("expected column name");
660                                });
661                            }
662                            None => {} // n.b. not all named columns need explicit names
663                        }
664                    }
665                }
666            }
667        }
668
669        Ok(expected.or(key))
670    }
671}
672
673fn impl_from_row(key: Key, name: &syn::Ident) -> proc_macro2::TokenStream {
674    let (trait_ty, column_ty) = match key {
675        Key::Index => (quote!(FromColumnsIndexed), quote!(ColumnsIndexed)),
676        Key::Name => (quote!(FromColumnsNamed), quote!(ColumnsNamed)),
677    };
678
679    quote! {
680        #[automatically_derived]
681        impl<C> ::aykroyd::FromRow<C> for #name
682        where
683            C: ::aykroyd::client::Client,
684            Self: ::aykroyd::row::#trait_ty<C>,
685        {
686            fn from_row(
687                row: &C::Row<'_>,
688            ) -> ::std::result::Result<Self, ::aykroyd::error::Error<C::Error>> {
689                ::aykroyd::row::#trait_ty::from_columns(
690                    ::aykroyd::row::#column_ty::new(row),
691                )
692            }
693        }
694    }
695}
696
697fn impl_from_columns(
698    key: Key,
699    name: &syn::Ident,
700    tuple_struct: bool,
701    fields: &[FieldInfo],
702) -> proc_macro2::TokenStream {
703    let mut wheres = vec![];
704    let mut num_const = 0;
705    let mut plus_nesteds = vec![];
706    let mut field_puts = vec![];
707    for (index, field) in fields.iter().enumerate() {
708        let ty = &field.ty;
709        let delegate = if field.nested {
710            Delegate::FromColumns
711        } else {
712            Delegate::FromColumn
713        };
714
715        {
716            use Delegate::*;
717            use Key::*;
718            let delegate = match (key, delegate) {
719                (Index, FromColumn) => quote!(::aykroyd::client::FromColumnIndexed),
720                (Index, FromColumns) => quote!(::aykroyd::row::FromColumnsIndexed),
721                (Name, FromColumn) => quote!(::aykroyd::client::FromColumnNamed),
722                (Name, FromColumns) => quote!(::aykroyd::row::FromColumnsNamed),
723            };
724            wheres.push(quote!(#ty: #delegate<C>));
725        }
726
727        {
728            let get_method = match delegate {
729                Delegate::FromColumn => quote!(get),
730                Delegate::FromColumns => quote!(get_nested),
731            };
732            let key = match key {
733                Key::Index => match &field.column {
734                    Some(index) => {
735                        quote!(#index)
736                    }
737                    None => {
738                        let num_const = syn::LitInt::new(
739                            &format!("{num_const}usize"),
740                            proc_macro2::Span::call_site(),
741                        );
742                        quote!(#num_const #(#plus_nesteds)*)
743                    }
744                },
745                Key::Name => match &field.column {
746                    Some(name) => {
747                        quote!(#name)
748                    }
749                    None => {
750                        let name = field
751                            .ident
752                            .as_ref()
753                            .map(ToString::to_string)
754                            .unwrap_or_else(|| index.to_string());
755
756                        let name = match delegate {
757                            Delegate::FromColumn => name,
758                            Delegate::FromColumns => {
759                                let mut s = name;
760                                s.push('_');
761                                s
762                            }
763                        };
764                        quote!(#name)
765                    }
766                },
767            };
768            field_puts.push(match &field.ident {
769                Some(field_name) => quote!(#field_name: columns.#get_method(#key)?),
770                None => quote!(columns.#get_method(#key)?),
771            });
772        }
773
774        if let Some(syn::Lit::Int(index)) = &field.column {
775            let index: usize = index.base10_parse().unwrap();
776            num_const = index;
777            plus_nesteds.clear();
778        }
779
780        match delegate {
781            Delegate::FromColumn => num_const += 1,
782            Delegate::FromColumns => plus_nesteds
783                .push(quote!(+ <#ty as ::aykroyd::row::FromColumnsIndexed<C>>::NUM_COLUMNS)),
784        }
785    }
786
787    let field_list = if !tuple_struct {
788        quote!({#(#field_puts),*})
789    } else if !field_puts.is_empty() {
790        quote!((#(#field_puts),*))
791    } else {
792        quote!()
793    };
794    let num_const = syn::LitInt::new(&format!("{num_const}usize"), proc_macro2::Span::call_site());
795
796    let (trait_ty, column_ty) = match key {
797        Key::Index => (quote!(FromColumnsIndexed), quote!(ColumnsIndexed)),
798        Key::Name => (quote!(FromColumnsNamed), quote!(ColumnsNamed)),
799    };
800
801    let num_columns = match key {
802        Key::Index => quote!(const NUM_COLUMNS: ::std::primitive::usize = #num_const #(#plus_nesteds)*;),
803        Key::Name => quote!(),
804    };
805
806    quote! {
807        #[automatically_derived]
808        impl<C> ::aykroyd::row::#trait_ty<C> for #name
809        where
810            C: ::aykroyd::client::Client,
811            #(#wheres),*
812        {
813            #num_columns
814
815            fn from_columns(
816                columns: ::aykroyd::row::#column_ty<C>,
817            ) -> ::std::result::Result<Self, ::aykroyd::error::Error<C::Error>> {
818                ::std::result::Result::Ok(#name #field_list)
819            }
820        }
821    }
822}