1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{
4 Data, DeriveInput, ExprPath, Field, Fields, Ident, Token, Type, parse::ParseStream,
5 parse_macro_input, spanned::Spanned,
6};
7
8const MACRO_NAME: &'static str = "frompg";
9
10#[proc_macro_derive(FromPg, attributes(frompg))]
13pub fn frompg(_item: TokenStream) -> TokenStream {
14 let input = parse_macro_input!(_item as DeriveInput);
15
16 let item_name = input.ident.clone();
17
18 let new_struct = match input.data {
19 Data::Struct(s) => from_pg_helper(&item_name, &s.fields),
20 _ => Err(syn::Error::new(
21 item_name.span(),
22 "frompg only supports structs",
23 )),
24 }
25 .unwrap_or_else(|e| e.into_compile_error());
26
27 new_struct.into()
28}
29
30fn from_pg_helper(
31 item_name: &Ident,
32 item_fields: &Fields,
33) -> syn::Result<proc_macro2::TokenStream> {
34 let conf_name = format_ident!("{}Config", item_name.clone());
35 let err_name = format_ident!("{}Error", item_name.clone());
36
37 if item_fields.is_empty() {
38 return Ok(quote! {
39 impl FromPg for #item_name {
40 type Config = ();
41 type Error = ::std::convert::Infallible;
42 fn from_pg(
43 _: &::tokio_postgres::row::Row,
44 _: &()
45 ) -> Result<Self, Self::Error> {
46 Ok(Self {})
47 }
48 }
49 });
50 }
51
52 let fields = map_fields(item_fields, |s| s.to_string())?;
53
54 let field_infos: Vec<_> = item_fields
55 .iter()
56 .map(|field| {
57 let fd = FieldDeserializer::from(field)?;
58 let field_name = field
59 .ident
60 .as_ref()
61 .ok_or(syn::Error::new(field.span(), "fields must be named"))?;
62
63 Ok(match fd {
64 Some(FieldDeserializer::Custom { ty, func }) => FieldInfo::Custom {
65 name: field_name.clone(),
66 ty,
67 func,
68 },
69 Some(FieldDeserializer::Derive) => {
70 let (derived_ty, is_option) = extract_option_inner_type(&field.ty);
71 FieldInfo::Derive {
72 name: field_name.clone(),
73 ty: derived_ty,
74 is_option,
75 }
76 }
77 _ => FieldInfo::Default {
78 name: field_name.clone(),
79 },
80 })
81 })
82 .collect::<syn::Result<Vec<_>>>()?;
83
84 let field_deserializations = field_infos
85 .iter()
86 .map(|field_info| {
87 match field_info {
88 FieldInfo::Custom { name, ty, func } => quote! {
89 row
90 .try_get::<_,#ty>(conf.#name.as_str())
91 .map_err(|e| Box::new(e) as Box<(dyn std::error::Error + Send + Sync)>)
92 .and_then(|val| #func(val).map_err(|e| e.into()))
93 },
94 FieldInfo::Derive { name, ty, is_option } => {
95 if *is_option {
96 quote! {
97 {
98 struct AnySql;
101 impl tokio_postgres::types::FromSql<'_> for AnySql {
102 fn from_sql(
103 _type_: &tokio_postgres::types::Type,
104 _raw: &[u8]
105 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
106 Ok(AnySql)
107 }
108
109 fn accepts(_type_: &tokio_postgres::types::Type) -> bool {
110 true
111 }
112 }
113
114 let has_non_null_fields = conf.#name.fields().iter().any(|&field_name| {
115 row.try_get::<_, AnySql>(field_name).is_ok()
116 });
117
118 if has_non_null_fields {
119 match <#ty as FromPg>::from_pg(row, &conf.#name) {
120 Ok(val) => Ok(Some(val)),
121 Err(e) => Err(Box::new(e) as Box<(dyn std::error::Error + Send + Sync)>)
122 }
123 } else {
124 Ok(None)
125 }
126 }
127 }
128 } else {
129 quote! {
130 <#ty as FromPg>::from_pg(row, &conf.#name)
131 .map_err(|e| Box::new(e) as Box<(dyn std::error::Error + Send + Sync)>)
132 }
133 }
134 },
135 FieldInfo::Default { name, .. } => quote! {
136 row
137 .try_get(conf.#name.as_str())
138 .map_err(|e| Box::new(e) as Box<(dyn std::error::Error + Send + Sync)>)
139 },
140 }
141 })
142 .collect::<Vec<_>>();
143
144 let config_fields = field_infos
145 .iter()
146 .map(|field_info| {
147 match field_info {
148 FieldInfo::Custom { name, .. } => quote! {
149 #name: String
150 },
151 FieldInfo::Derive { name, ty, .. } => quote! {
152 #name: <#ty as FromPg>::Config
153 },
154 FieldInfo::Default { name, .. } => quote! {
155 #name: String
156 },
157 }
158 })
159 .collect::<Vec<_>>();
160
161 let default_fields = field_infos
162 .iter()
163 .map(|field_info| {
164 match field_info {
165 FieldInfo::Custom { name, .. } => quote! {
166 #name: String::from(stringify!(#name))
167 },
168 FieldInfo::Derive { name, ty, .. } => quote! {
169 #name: <#ty as FromPg>::Config::default()
170 },
171 FieldInfo::Default { name, .. } => quote! {
172 #name: String::from(stringify!(#name))
173 },
174 }
175 })
176 .collect::<Vec<_>>();
177
178 let getter_methods = field_infos
179 .iter()
180 .map(|field_info| {
181 match field_info {
182 FieldInfo::Custom { name, .. } => quote! {
183 pub fn #name(&self) -> &str {
184 self.#name.as_ref()
185 }
186 },
187 FieldInfo::Derive { name, ty, .. } => quote! {
188 pub fn #name(&self) -> &<#ty as FromPg>::Config {
189 &self.#name
190 }
191 },
192 FieldInfo::Default { name, .. } => quote! {
193 pub fn #name(&self) -> &str {
194 self.#name.as_ref()
195 }
196 },
197 }
198 })
199 .collect::<Vec<_>>();
200
201 let fields_method = {
203 let field_getters: Vec<_> = field_infos
204 .iter()
205 .filter_map(|field_info| match field_info {
206 FieldInfo::Custom { name, .. } => Some(quote! { self.#name() }),
207 FieldInfo::Default { name, .. } => Some(quote! { self.#name() }),
208 FieldInfo::Derive { .. } => None, })
210 .collect();
211
212 quote! {
213 pub fn fields(&self) -> Vec<&str> {
214 vec![#( #field_getters ),*]
215 }
216 }
217 };
218
219 let setter_methods = field_infos
220 .iter()
221 .map(|field_info| {
222 match field_info {
223 FieldInfo::Custom { name, .. } => {
224 let set_method = format_ident!("set_{}", name);
225 quote! {
226 pub fn #set_method(self, name: String) -> Self {
227 Self {
228 #name: name,
229 ..self
230 }
231 }
232 }
233 }
234 FieldInfo::Derive { name, ty, .. } => {
235 let set_method = format_ident!("set_{}", name);
236 quote! {
237 pub fn #set_method(self, config: <#ty as FromPg>::Config) -> Self {
238 Self {
239 #name: config,
240 ..self
241 }
242 }
243 }
244 }
245 FieldInfo::Default { name, .. } => {
246 let set_method = format_ident!("set_{}", name);
247 quote! {
248 pub fn #set_method(self, name: String) -> Self {
249 Self {
250 #name: name,
251 ..self
252 }
253 }
254 }
255 }
256 }
257 })
258 .collect::<Vec<_>>();
259
260 let prefix_methods = field_infos
261 .iter()
262 .filter_map(|field_info| {
263 match field_info {
264 FieldInfo::Custom { name, .. } => {
265 let prefix_method = format_ident!("prefix_{}", name);
266 Some(quote! {
267 pub fn #prefix_method(self, prefix: String) -> Self {
268 let mut name = prefix.clone();
269 name.push_str(stringify!(#name));
270 Self {
271 #name: name,
272 ..self
273 }
274 }
275 })
276 }
277 FieldInfo::Derive { .. } => {
278 None
279 }
280 FieldInfo::Default { name, .. } => {
281 let prefix_method = format_ident!("prefix_{}", name);
282 Some(quote! {
283 pub fn #prefix_method(self, prefix: String) -> Self {
284 let mut name = prefix.clone();
285 name.push_str(stringify!(#name));
286 Self {
287 #name: name,
288 ..self
289 }
290 }
291 })
292 }
293 }
294 }).collect::<Vec<_>>();
295
296 Ok(quote! {
297 #[derive(Clone, Debug, Eq, PartialEq)]
298 pub struct #conf_name {
299 #( #config_fields ),*
300 }
301
302 impl ::std::default::Default for #conf_name {
303 fn default() -> Self {
304 Self {
305 #( #default_fields ),*
306 }
307 }
308 }
309
310 impl #conf_name {
311 pub fn new() -> Self {
312 Self::default()
313 }
314
315 #( #getter_methods )*
316
317 #( #setter_methods )*
318
319 #( #prefix_methods )*
320
321 #fields_method
322 }
323
324 #[derive(Debug, Default)]
325 pub struct #err_name {
326 #(
327 #fields: Option<Box<dyn ::std::error::Error + ::core::marker::Sync + ::core::marker::Send>>
328 ),*
329 }
330
331 impl ::std::fmt::Display for #err_name {
332 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
333 let mut messages = Vec::new();
334 #(
335 if let Some(ref err) = self.#fields {
336 messages.push(format!("{}: {}", stringify!(#fields), err));
337 }
338 )*
339 write!(f, "{}", messages.join("; "))
340 }
341 }
342
343 impl ::std::error::Error for #err_name {}
344
345 impl FromPg for #item_name {
346 type Config = #conf_name;
347 type Error = #err_name;
348
349 fn from_pg(row: &::tokio_postgres::row::Row, conf: &Self::Config) -> Result<Self, Self::Error> {
350 match ( #( #field_deserializations ),* ) {
351 ( #( Ok(#fields) ),* ) => Ok(Self {
352 #( #fields ),*
353 }),
354 ( #( #fields ),* ) => Err(Self::Error {
355 #( #fields: #fields.err() ),*
356 })
357 }
358 }
359 }
360 })
361}
362
363enum FieldInfo {
364 Custom {
365 name: Ident,
366 ty: Type,
367 func: ExprPath,
368 },
369 Derive {
370 name: Ident,
371 ty: Type,
372 is_option: bool,
373 },
374 Default {
375 name: Ident,
376 },
377}
378
379fn extract_option_inner_type(ty: &Type) -> (Type, bool) {
381 if let Type::Path(type_path) = ty {
382 if let Some(segment) = type_path.path.segments.last() {
383 if segment.ident == "Option" {
384 if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
385 if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
386 return (inner_ty.clone(), true);
387 }
388 }
389 }
390 }
391 }
392 (ty.clone(), false)
393}
394
395enum FieldDeserializer {
396 Custom { ty: Type, func: ExprPath },
397 Derive,
398}
399
400impl FieldDeserializer {
401 fn from(field: &Field) -> syn::Result<Option<Self>> {
402 field
403 .attrs
404 .iter()
405 .find(|attr| attr.path().is_ident(MACRO_NAME))
406 .map(|attr| {
407 Ok(attr
408 .meta
409 .require_list()?
410 .parse_args::<FieldDeserializer>()?)
411 })
412 .transpose()
413 }
414}
415
416impl syn::parse::Parse for FieldDeserializer {
420 fn parse(input: ParseStream) -> syn::Result<Self> {
421 let first_token: Ident = input.parse()?;
422
423 match first_token.to_string().as_str() {
424 "from" => {
425 input.parse::<Token![=]>()?;
426 let ty: Type = input.parse()?;
427 input.parse::<Token![,]>()?;
428 let func_token: Ident = input.parse()?;
429 if func_token != "func" {
430 return Err(syn::Error::new(func_token.span(), "expected `func`"));
431 }
432 input.parse::<Token![=]>()?;
433 let func: ExprPath = input.parse()?;
434 Ok(Self::Custom { ty, func })
435 }
436 "derive" => {
437 input.parse::<Token![=]>()?;
438 let _ty: Type = input.parse()?;
439 Ok(Self::Derive)
440 }
441 _ => Err(syn::Error::new(
442 first_token.span(),
443 "expected `from` or `derive`",
444 )),
445 }
446 }
447}
448
449fn map_fields(fields: &Fields, f: impl Fn(&str) -> String) -> syn::Result<Vec<Ident>> {
452 fields
453 .iter()
454 .map(|field| {
455 Ok(Ident::new(
456 &f(&field
457 .ident
458 .as_ref()
459 .ok_or(syn::Error::new(fields.span(), "fields must be named"))?
460 .to_string()),
461 field.ident.span(),
462 ))
463 })
464 .collect()
465}