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}