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 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
189impl 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
210fn 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}