|
1 | 1 | use darling::ast::{self, Style}; |
2 | | -use darling::{FromDeriveInput, FromField}; |
| 2 | +use darling::{FromDeriveInput, FromField, ToTokens}; |
3 | 3 | use proc_macro::TokenStream; |
4 | 4 | use quote::quote; |
5 | 5 | use syn::{parse_macro_input, DeriveInput, Ident}; |
@@ -60,18 +60,33 @@ impl DeriveFromRow { |
60 | 60 | Style::Struct => fields.fields, |
61 | 61 | }; |
62 | 62 |
|
63 | | - let from_row_fields = fields.iter().map(|f| f.generate_from_row(&module)); |
64 | | - let try_from_row_fields = fields.iter().map(|f| f.generate_try_from_row(&module)); |
| 63 | + let from_row_fields = fields |
| 64 | + .iter() |
| 65 | + .map(|f| f.generate_from_row(&module)) |
| 66 | + .collect::<syn::Result<Vec<_>>>()?; |
| 67 | + |
| 68 | + let try_from_row_fields = fields |
| 69 | + .iter() |
| 70 | + .map(|f| f.generate_try_from_row(&module)) |
| 71 | + .collect::<syn::Result<Vec<_>>>()?; |
| 72 | + |
65 | 73 | let original_predicates = where_clause.clone().map(|w| &w.predicates).into_iter(); |
66 | 74 | let mut predicates = Vec::new(); |
67 | 75 |
|
68 | 76 | for field in fields.iter() { |
| 77 | + let target_ty = &field.target_ty()?; |
69 | 78 | let ty = &field.ty; |
70 | 79 | predicates.push(if field.flatten { |
71 | | - quote! (#ty: postgres_from_row::FromRow) |
| 80 | + quote! (#target_ty: postgres_from_row::FromRow) |
72 | 81 | } else { |
73 | | - quote! (#ty: for<'a> #module::types::FromSql<'a>) |
| 82 | + quote! (#target_ty: for<'a> #module::types::FromSql<'a>) |
74 | 83 | }); |
| 84 | + |
| 85 | + if field.from.is_some() { |
| 86 | + predicates.push(quote!(#ty: std::convert::From<#target_ty>)) |
| 87 | + } else if field.try_from.is_some() { |
| 88 | + predicates.push(quote!(#ty: std::convert::From<#target_ty>)) |
| 89 | + } |
75 | 90 | } |
76 | 91 |
|
77 | 92 | Ok(quote! { |
@@ -101,38 +116,61 @@ struct FromRowField { |
101 | 116 | ty: syn::Type, |
102 | 117 | #[darling(default)] |
103 | 118 | flatten: bool, |
| 119 | + try_from: Option<String>, |
| 120 | + from: Option<String>, |
104 | 121 | } |
105 | 122 |
|
106 | 123 | impl FromRowField { |
107 | | - fn generate_from_row(&self, module: &Ident) -> proc_macro2::TokenStream { |
| 124 | + fn target_ty(&self) -> syn::Result<proc_macro2::TokenStream> { |
| 125 | + if let Some(from) = &self.from { |
| 126 | + Ok(from.parse()?) |
| 127 | + } else if let Some(try_from) = &self.try_from { |
| 128 | + Ok(try_from.parse()?) |
| 129 | + } else { |
| 130 | + Ok(self.ty.to_token_stream()) |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + fn generate_from_row(&self, module: &Ident) -> syn::Result<proc_macro2::TokenStream> { |
108 | 135 | let ident = self.ident.as_ref().unwrap(); |
109 | 136 | let str_ident = ident.to_string(); |
110 | | - let ty = &self.ty; |
| 137 | + let field_ty = &self.ty; |
111 | 138 |
|
112 | | - if self.flatten { |
113 | | - quote! { |
114 | | - #ident: <#ty as postgres_from_row::FromRow>::from_row(row) |
115 | | - } |
| 139 | + let target_ty = self.target_ty()?; |
| 140 | + |
| 141 | + let mut base = if self.flatten { |
| 142 | + quote!(<#target_ty as postgres_from_row::FromRow>::from_row(row)) |
116 | 143 | } else { |
117 | | - quote! { |
118 | | - #ident: #module::Row::get::<&str, #ty>(row, #str_ident) |
119 | | - } |
120 | | - } |
| 144 | + quote!(#module::Row::get::<&str, #target_ty>(row, #str_ident)) |
| 145 | + }; |
| 146 | + |
| 147 | + if self.from.is_some() { |
| 148 | + base = quote!(<#field_ty as std::convert::From<#target_ty>>::from(#base)); |
| 149 | + } else if self.try_from.is_some() { |
| 150 | + base = quote!(<#field_ty as std::convert::TryFrom<#target_ty>>::try_from(#base).expect("could not convert column")); |
| 151 | + }; |
| 152 | + |
| 153 | + Ok(quote!(#ident: #base)) |
121 | 154 | } |
122 | 155 |
|
123 | | - fn generate_try_from_row(&self, module: &Ident) -> proc_macro2::TokenStream { |
| 156 | + fn generate_try_from_row(&self, module: &Ident) -> syn::Result<proc_macro2::TokenStream> { |
124 | 157 | let ident = self.ident.as_ref().unwrap(); |
125 | 158 | let str_ident = ident.to_string(); |
126 | | - let ty = &self.ty; |
| 159 | + let field_ty = &self.ty; |
| 160 | + let target_ty = self.target_ty()?; |
127 | 161 |
|
128 | | - if self.flatten { |
129 | | - quote! { |
130 | | - #ident: <#ty as postgres_from_row::FromRow>::try_from_row(row)? |
131 | | - } |
| 162 | + let mut base = if self.flatten { |
| 163 | + quote!(<#target_ty as postgres_from_row::FromRow>::try_from_row(row)?) |
132 | 164 | } else { |
133 | | - quote! { |
134 | | - #ident: #module::Row::try_get::<&str, #ty>(row, #str_ident)? |
135 | | - } |
136 | | - } |
| 165 | + quote!(#module::Row::try_get::<&str, #target_ty>(row, #str_ident)?) |
| 166 | + }; |
| 167 | + |
| 168 | + if self.from.is_some() { |
| 169 | + base = quote!(<#field_ty as std::convert::From<#target_ty>>::from(#base)); |
| 170 | + } else if self.try_from.is_some() { |
| 171 | + base = quote!(<#field_ty as std::convert::TryFrom<#target_ty>>::try_from(#base)?); |
| 172 | + }; |
| 173 | + |
| 174 | + Ok(quote!(#ident: #base)) |
137 | 175 | } |
138 | 176 | } |
0 commit comments