tiberius_mappers_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, Field, Ident};
4
5fn impl_from_trait_for_row(ast: DeriveInput) -> proc_macro2::TokenStream {
6    let ident: Ident = ast.ident;
7
8    let mut fields: Vec<Field> = vec![];
9
10    match ast.data {
11        syn::Data::Struct(data) => {
12            for field in data.fields {
13                if field.ident.is_some() {
14                    fields.push(field)
15                }
16            }
17        }
18        _ => panic!("Only structs are supported by tiberius mappers derive"),
19    };
20
21    let field_mappers: Vec<proc_macro2::TokenStream> = fields
22        .into_iter()
23        .enumerate()
24        .map(|(idx, field)| {
25            let f_ident = field.ident.unwrap();
26            let f_type = field.ty;
27            // This is very closely based on code from tiberius_derive
28            quote! {
29                    #f_ident: {
30                        macro_rules! read_data {
31                            (Option<$f_type: ty>) => { {
32                                    <$f_type as tiberius::FromSqlOwned>::from_sql_owned(
33                                        row_iter.next().ok_or_else(|| tiberius::error::Error::Conversion(format!("Could not find field {} from column with index {}", stringify!(#f_ident), #idx).into()))?
34                            ).map_err(|e| tiberius::error::Error::Conversion(format!("Could not convert type for optional field {} from column index {} with underlying error {}", stringify!(#f_ident), #idx, e).into()))?
35                               } };
36                            ($f_type: ty) => { {
37                                (<$f_type as tiberius::FromSqlOwned>::from_sql_owned(
38                                    row_iter.next().ok_or_else(|| tiberius::error::Error::Conversion(format!("Could not find field {} from column with index {}", stringify!(#f_ident), #idx).into()))?
39                                ).map_err(|e| tiberius::error::Error::Conversion(format!("Could not convert type for non optional field {} from column index {} with underlying error {}", stringify!(#f_ident), #idx, e).into()))?
40                                ).ok_or_else(|| tiberius::error::Error::Conversion(format!(r"None value for non optional field {} from column with index {}", stringify!(#f_ident), #idx).into()))?
41                            }};
42                        };
43
44                        read_data!(#f_type)
45                }
46            }
47        })
48        .collect::<Vec<_>>();
49
50    quote! {
51        impl TryFromRow for #ident {
52
53            fn try_from_row(row: tiberius::Row) -> Result<Self, tiberius::error::Error> where Self: Sized {
54                let mut row_iter = row.into_iter();
55                Ok(Self {
56                    #(#field_mappers,)*
57                })
58            }
59        }
60    }
61}
62
63#[proc_macro_derive(TryFromRow)]
64pub fn from_row_derive_macro_owned(input: TokenStream) -> TokenStream {
65    let ast: DeriveInput = syn::parse(input).unwrap();
66    impl_from_trait_for_row(ast).into()
67}