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#[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#[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#[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(¶m) {
343 has_index.remove(¶m).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#[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#[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#[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 => {} }
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}