|
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