tiberius_derive/
lib.rs

1use crate::from_row_opts::FromRowOpts;
2use darling::{ast::Fields, usage::GenericsExt, FromDeriveInput};
3use from_row_opts::RenameRule;
4use proc_macro::{self, TokenStream};
5use proc_macro2::{Ident, Literal};
6use quote::quote;
7use syn::{parse_macro_input, DeriveInput, Field, Variant};
8mod from_row_opts;
9
10#[proc_macro_derive(FromRow, attributes(tiberius_derive))]
11pub fn from_row(input: TokenStream) -> TokenStream {
12    let derive_input: DeriveInput = parse_macro_input!(input);
13
14    let FromRowOpts {
15        ident,
16        attrs: _,
17        owned,
18        data,
19        by_position,
20        generics,
21        rename_all,
22        auto
23    } = FromRowOpts::<syn::Generics, Variant, Field>::from_derive_input(&derive_input).unwrap();
24
25    let generics: syn::Generics = generics;
26    let auto = auto.is_some();
27    let lifetimes = generics.declared_lifetimes();
28
29    let fields = match data {
30        darling::ast::Data::Struct(Fields { fields, .. }) => fields.into_iter(),
31        _ => unimplemented!(),
32    };
33
34    let fields = if owned.is_some() {
35        try_get_rows_from_iter_owned(fields)
36    } else if by_position.is_some() {
37        try_get_rows_by_index(fields, auto)
38    }else{
39        try_get_rows_by_key(fields, rename_all, auto)
40    };
41
42    let expanded = if owned.is_some() {
43        expand_owned(ident, fields)
44    } else if lifetimes.len() == 1 {
45        expand_borrowed(ident, fields)
46    } else {
47        expand_copy(ident, fields)
48    };
49
50    expanded.into()
51}
52
53fn try_get_rows_from_iter_owned(fields: std::vec::IntoIter<Field>) -> Vec<proc_macro2::TokenStream> {
54    fields.enumerate().map(|(idx, field)| {
55        let f_ident = field.ident.unwrap();
56        let f_type = field.ty;
57
58        
59        quote! {
60        #f_ident: {
61            macro_rules! unwrap_nullable {
62                (Option<$f_type: ty>) => {
63                    <$f_type as tiberius::FromSqlOwned>::from_sql_owned(row_iter.next().ok_or_else(
64                        || tiberius::error::Error::Conversion(
65                            format!("Could not find field {} from column with index {}", stringify!(#f_ident), #idx).into()
66                        )
67                    )?)?
68                    };
69                ($f_type: ty) => {
70                    (<$f_type as tiberius::FromSqlOwned>::from_sql_owned(row_iter.next().ok_or_else(
71                        || tiberius::error::Error::Conversion(
72                            format!("Could not find field {} from column with index {}", stringify!(#f_ident), #idx).into()
73                        )
74                    )?)?).ok_or_else(
75                        || tiberius::error::Error::Conversion(
76                            format!(r" None value for non optional field {} from column with index {}", stringify!(#f_ident), #idx).into()
77                        )
78                    )?
79                };
80            };
81
82            unwrap_nullable!(#f_type)
83            }
84        }
85    }).collect::<Vec<_>>()
86}
87
88fn try_get_rows_by_index(fields: std::vec::IntoIter<Field>, auto: bool) -> Vec<proc_macro2::TokenStream> {
89    fields.enumerate().map(|(idx,field)| {
90        let f_ident = field.ident.unwrap();
91        let f_type = field.ty;
92
93        let idx_lit = Literal::usize_suffixed(idx);
94        if auto {
95            quote! {
96                #f_ident: {
97                    macro_rules! unwrap_nullable {
98                        (String) => {
99                            row.try_get::<&str, usize>(#idx_lit)?.ok_or_else(
100                                || tiberius::error::Error::Conversion(
101                                    format!(r" None value for non optional field {} from column with index {}", stringify!(#f_ident), #idx_lit).into()
102                                    )
103                                )?.to_owned()
104                        };
105                        (Option<String>) => {
106                            row.try_get::<&str, usize>(#idx_lit)?.map(|s| s.to_owned())
107                        };
108                        (Option<$f_type: ty>) => {
109                            row.try_get(#idx_lit)?
110                        };
111                        ($f_type: ty) => {
112                            row.try_get(#idx_lit)?.ok_or_else(
113                                || tiberius::error::Error::Conversion(
114                                    format!(r" None value for non optional field {} from column with index {}", stringify!(#f_ident), #idx_lit).into()
115                                    )
116                                )?
117                        };
118                    };
119                    unwrap_nullable!(#f_type)
120                    }
121                }
122        }
123        else {
124            quote! {
125            #f_ident: {
126                macro_rules! unwrap_nullable {
127                    (Option<$f_type: ty>) => {
128                        row.try_get(#idx_lit)?
129                    };
130                    ($f_type: ty) => {
131                        row.try_get(#idx_lit)?.ok_or_else(
132                            || tiberius::error::Error::Conversion(
133                                format!(r" None value for non optional field {} from column with index {}", stringify!(#f_ident), #idx_lit).into()
134                                )
135                            )?
136                    };
137                };
138                unwrap_nullable!(#f_type)
139                }
140            }
141
142        }
143    }).collect::<Vec<_>>()
144}
145
146fn try_get_rows_by_key(fields: std::vec::IntoIter<Field>, rename_rule: RenameRule, auto: bool) -> Vec<proc_macro2::TokenStream> {
147    fields.map(|field| {
148        let f_ident =  field.ident.unwrap();
149        let f_type = field.ty;
150        let f_ident_string = &f_ident.to_string();
151        let field_renamed = &rename_rule.0.apply_to_field(f_ident_string);
152
153        if auto {
154            quote! {
155                #f_ident: {
156                    macro_rules! unwrap_nullable {
157                        (String) => {
158                            row.try_get::<&str, &str>(#field_renamed)?.ok_or_else(
159                                || tiberius::error::Error::Conversion(
160                                    format!(r" None value for non optional field {}", stringify!(#f_ident)).into()
161                                )
162                            )?.to_owned()
163                        };
164                        (Option<String>) => {
165                            row.try_get::<&str, &str>(#field_renamed)?.map(|s| s.to_owned())
166                        };
167                        (Option<$f_type: ty>) => {
168                            row.try_get(#field_renamed)?
169                        };
170                        ($f_type: ty) => {
171                            row.try_get(#field_renamed)?
172                            .ok_or_else(
173                                || tiberius::error::Error::Conversion(
174                                    format!(r" None value for non optional field {}", stringify!(#f_ident)).into()
175                                )
176                            )?          
177                        };
178                    };
179                    
180                    unwrap_nullable!(#f_type)
181                }
182            }
183        } else {
184            quote! {
185                #f_ident: {
186                    macro_rules! unwrap_nullable {
187                        (Option<$f_type: ty>) => {
188                            row.try_get(#field_renamed)?
189                        };
190                        ($f_type: ty) => {
191                            row.try_get(#field_renamed)?
192                            .ok_or_else(
193                                || tiberius::error::Error::Conversion(
194                                    format!(r" None value for non optional field {}", stringify!(#f_ident)).into()
195                                )
196                            )?          
197                        };
198                    };
199                    
200                    unwrap_nullable!(#f_type)
201                }
202            }
203        }
204        
205    }).collect::<Vec<_>>()
206}
207
208fn expand_owned(ident: Ident, fields: Vec<proc_macro2::TokenStream>) -> proc_macro2::TokenStream {
209    quote! {
210        impl #ident{
211            pub fn from_row(row: tiberius::Row) -> Result<Self, tiberius::error::Error> {
212                let mut row_iter = row.into_iter();
213
214                Ok(Self{
215                    #(#fields,)*
216                })
217            }
218        }
219    }
220}
221
222fn expand_borrowed(
223    ident: Ident,
224    fields: Vec<proc_macro2::TokenStream>,
225) -> proc_macro2::TokenStream {
226    quote! {
227        impl<'a> #ident<'a>{
228            pub fn from_row(row: &'a tiberius::Row) -> Result<Self, tiberius::error::Error> {
229                Ok(Self{
230                    #(#fields,)*
231                })
232            }
233        }
234    }
235}
236
237fn expand_copy(ident: Ident, fields: Vec<proc_macro2::TokenStream>) -> proc_macro2::TokenStream {
238    quote! {
239        impl #ident{
240            pub fn from_row(row: &tiberius::Row) -> Result<Self, tiberius::error::Error> {
241                Ok(Self{
242                    #(#fields,)*
243                })
244            }
245        }
246    }
247}