from_pg_derive/
lib.rs

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/// For a type using the frompg macro, you can write `#[frompg(from = T, func = F)] above a field`.
11/// This retrieves any valid attributes from a field, and can return an error.
12#[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    let set_fields = map_fields(item_fields, |s| format!("set_{s}"))?;
54    let prefix_fields = map_fields(item_fields, |s| format!("prefix_{s}"))?;
55
56    let field_deserializations = item_fields
57        .iter()
58        .map(|field| {
59            let fd = FieldDeserializer::from(field)?;
60            let field_name = field
61                .ident
62                .as_ref()
63                .ok_or(syn::Error::new(field.span(), "fields must be named"))?;
64
65            Ok(match fd {
66                Some(FieldDeserializer { ty, func }) => quote! {
67                    row
68                        .try_get::<_,#ty>(conf.#field_name.as_str())
69                        .map_err(|e| Box::new(e) as Box<(dyn std::error::Error + Send + Sync)>)
70                        .and_then(|val| #func(val).map_err(|e| e.into()))
71                },
72                _ => quote! {
73                    row
74                        .try_get(conf.#field_name.as_str())
75                        .map_err(|e| Box::new(e) as Box<(dyn std::error::Error + Send + Sync)>)
76                },
77            })
78        })
79        .collect::<syn::Result<Vec<_>>>()?;
80
81    Ok(quote! {
82        #[derive(Clone, Debug, Eq, PartialEq)]
83        pub struct #conf_name {
84            #(
85                #fields: String
86            ),*
87        }
88
89        impl ::std::default::Default for #conf_name {
90            fn default() -> Self {
91                Self {
92                    #(
93                        #fields: String::from(stringify!(#fields))
94                    ),*
95                }
96            }
97        }
98
99        impl #conf_name {
100            pub fn new() -> Self {
101                Self::default()
102            }
103
104            #(
105                pub fn #fields(&self) -> &str {
106                    self.#fields.as_ref()
107                }
108
109                pub fn #set_fields(self, name: String) -> Self {
110                    Self {
111                        #fields: name,
112                        ..self
113                    }
114                }
115
116                pub fn #prefix_fields(self, prefix: String) -> Self {
117                    let mut name = prefix.clone();
118                    name.push_str(stringify!(#fields));
119                    Self {
120                        #fields: name,
121                        ..self
122                    }
123                }
124            )*
125        }
126
127        #[derive(Debug, Default)]
128        pub struct #err_name {
129            #(
130                #fields: Option<Box<dyn ::std::error::Error + ::core::marker::Sync + ::core::marker::Send>>
131            ),*
132        }
133
134        impl ::std::fmt::Display for #err_name {
135            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
136                let mut messages = Vec::new();
137                #(
138                    if let Some(ref err) = self.#fields {
139                        messages.push(format!("{}: {}", stringify!(#fields), err));
140                    }
141                )*
142                write!(f, "{}", messages.join("; "))
143            }
144        }
145
146        impl ::std::error::Error for #err_name {}
147
148        impl FromPg for #item_name {
149            type Config = #conf_name;
150            type Error = #err_name;
151
152            fn from_pg(row: &::tokio_postgres::row::Row, conf: &Self::Config) -> Result<Self, Self::Error> {
153                match ( #( #field_deserializations ),* ) {
154                    ( #( Ok(#fields) ),* ) => Ok(Self {
155                        #( #fields ),*
156                    }),
157                    ( #( #fields ),* ) => Err(Self::Error {
158                        #(
159                            #fields: #fields.err()
160                        ),*
161                    })
162                }
163            }
164        }
165    })
166}
167
168struct FieldDeserializer {
169    pub ty: Type,
170    pub func: ExprPath,
171}
172
173impl FieldDeserializer {
174    fn from(field: &Field) -> syn::Result<Option<Self>> {
175        field
176            .attrs
177            .iter()
178            .find(|attr| attr.path().is_ident(MACRO_NAME))
179            .map(|attr| {
180                Ok(attr
181                    .meta
182                    .require_list()?
183                    .parse_args::<FieldDeserializer>()?)
184            })
185            .transpose()
186    }
187}
188
189/// For a type using the frompg macro, you can write `#[frompg(from = T, func = F)] above a field`.
190/// This retrieves any valid attributes from a field, and can return an error.
191impl syn::parse::Parse for FieldDeserializer {
192    fn parse(input: ParseStream) -> syn::Result<Self> {
193        let from_token: Ident = input.parse()?;
194        if from_token != "from" {
195            return Err(syn::Error::new(from_token.span(), "expected `from`"));
196        }
197        input.parse::<Token![=]>()?;
198        let ty: Type = input.parse()?;
199        input.parse::<Token![,]>()?;
200        let func_token: Ident = input.parse()?;
201        if func_token != "func" {
202            return Err(syn::Error::new(func_token.span(), "expected `func`"));
203        }
204        input.parse::<Token![=]>()?;
205        let func: ExprPath = input.parse()?;
206        Ok(Self { ty, func })
207    }
208}
209
210/// Maps the fields of a DataStruct using the given function.
211/// Returns `None` if any fields are `None`.
212fn map_fields(fields: &Fields, f: impl Fn(&str) -> String) -> syn::Result<Vec<Ident>> {
213    fields
214        .iter()
215        .map(|field| {
216            Ok(Ident::new(
217                &f(&field
218                    .ident
219                    .as_ref()
220                    .ok_or(syn::Error::new(fields.span(), "fields must be named"))?
221                    .to_string()),
222                field.ident.span(),
223            ))
224        })
225        .collect()
226}