postgres_mapper_derive/
lib.rs

1extern crate quote;
2extern crate proc_macro;
3#[macro_use]
4extern crate syn;
5
6use proc_macro::TokenStream;
7use quote::Tokens;
8
9use syn::DeriveInput;
10use syn::Meta::{List, NameValue};
11use syn::NestedMeta::{Literal, Meta};
12use syn::Data::*;
13
14use syn::{Fields, Ident};
15
16#[proc_macro_derive(PostgresMapper, attributes(pg_mapper))]
17pub fn postgres_mapper(input: TokenStream) -> TokenStream {
18    let ast = parse_macro_input!(input as DeriveInput);
19
20    impl_derive(&ast)
21        .parse()
22        .expect("Error parsing postgres mapper tokens")
23}
24
25fn impl_derive(ast: &DeriveInput) -> Tokens {
26    #[allow(unused_mut)]
27    let mut tokens = Tokens::new();
28
29
30
31    #[allow(unused_variables)]
32    let fields: &Fields = match ast.data {
33        Struct(ref s) => {
34            &s.fields
35        },
36        Enum(ref u) => {panic!("Enums can not be mapped")},
37        Union(ref u) => {panic!("Unions can not be mapped")},
38    };
39
40    #[allow(unused_variables)]
41    let table_name = parse_table_attr(&ast);
42
43    #[cfg(feature = "postgres-support")]
44    {
45        impl_from_row(&mut tokens, &ast.ident, &fields);
46        impl_from_borrowed_row(&mut tokens, &ast.ident, &fields);
47
48        #[cfg(feature = "postgres-mapper")]
49        {
50            impl_postgres_mapper(&mut tokens, &ast.ident, &fields, &table_name);
51        }
52    }
53
54    #[cfg(feature = "tokio-postgres-support")]
55    {
56        impl_tokio_from_row(&mut tokens, &ast.ident, &fields);
57        impl_tokio_from_borrowed_row(&mut tokens, &ast.ident, &fields);
58
59        #[cfg(feature = "postgres-mapper")]
60        {
61            impl_tokio_postgres_mapper(&mut tokens, &ast.ident, &fields, &table_name);
62        }
63    }
64
65    tokens
66}
67
68#[cfg(feature = "postgres-support")]
69fn impl_from_row(t: &mut Tokens, struct_ident: &Ident, fields: &Fields) {
70    t.append(format!("
71impl<'a> From<::postgres::rows::Row<'a>> for {struct_name} {{
72    fn from(row: ::postgres::rows::Row<'a>) -> Self {{
73        Self {{", struct_name=struct_ident));
74
75    for field in fields {
76        let ident = field.ident.clone().expect("Expected structfield identifier");
77
78        t.append(format!("
79            {0}: row.get(\"{0}\"),", ident));
80    }
81
82    t.append("
83        }
84    }
85}");
86}
87
88#[cfg(feature = "postgres-support")]
89fn impl_from_borrowed_row(t: &mut Tokens, struct_ident: &Ident, fields: &Fields) {
90    t.append(format!("
91impl<'a> From<&'a ::postgres::rows::Row<'a>> for {struct_name} {{
92    fn from(row: &::postgres::rows::Row<'a>) -> Self {{
93        Self {{", struct_name=struct_ident));
94
95    for field in fields {
96        let ident = field.ident.clone().expect("Expected structfield identifier");
97
98        t.append(format!("
99            {0}: row.get(\"{0}\"),", ident));
100    }
101
102    t.append("
103        }
104    }
105}");
106}
107
108#[cfg(all(feature = "postgres-support", feature = "postgres-mapper"))]
109fn impl_postgres_mapper(t: &mut Tokens, struct_ident: &Ident, fields: &Fields, table_name: &str) {
110    t.append(format!("
111impl ::postgres_mapper::FromPostgresRow for {struct_name} {{
112    fn from_postgres_row(row: ::postgres::rows::Row)
113        -> Result<Self, ::postgres_mapper::Error> {{
114        Ok(Self {{", struct_name=struct_ident));
115
116    for field in fields {
117        let ident = field.ident.clone().expect("Expected structfield identifier");
118
119        t.append(format!("
120            {0}: row.get_opt(\"{0}\").ok_or_else(|| ::postgres_mapper::Error::ColumnNotFound)??,", ident));
121    }
122
123    t.append("
124        })
125    }
126
127    fn from_postgres_row_ref(row: &::postgres::rows::Row)
128        -> Result<Self, ::postgres_mapper::Error> {
129        Ok(Self {");
130
131    for field in fields {
132        let ident = field.ident.clone().expect("Expected structfield identifier");
133
134        t.append(format!("
135            {0}: row.get_opt(\"{0}\").ok_or_else(|| ::postgres_mapper::Error::ColumnNotFound)??,", ident));
136    }
137
138    t.append("
139        })
140    }");
141
142    t.append(format!(
143    "fn sql_table() -> String {{
144        \" {0} \".to_string()
145    }}"
146    , table_name));
147
148    t.append(
149    format!(
150    "fn sql_fields() -> String {{")
151    );
152
153    let field_name = fields.iter().map(|field| {
154        let ident = field.ident.clone().expect("Expected structfield identifier");
155        format!("{0}.{1}", table_name, ident)
156    }).collect::<Vec<String>>().join(", ");
157
158    t.append(format!("\" {0} \".to_string()", field_name));
159
160    t.append(
161    "}"
162    );
163
164    t.append("
165}");
166}
167
168#[cfg(feature = "tokio-postgres-support")]
169fn impl_tokio_from_row(t: &mut Tokens, struct_ident: &Ident, fields: &Fields) {
170    t.append(format!("
171impl From<::tokio_postgres::rows::Row> for {struct_name} {{
172    fn from(row: ::tokio_postgres::rows::Row) -> Self {{
173        Self {{", struct_name=struct_ident));
174
175    for field in fields {
176        let ident = field.ident.clone().expect("Expected structfield identifier");
177
178        t.append(format!("
179            {0}: row.get(\"{0}\"),", ident));
180    }
181
182    t.append("
183        }
184    }
185}");
186}
187
188#[cfg(feature = "tokio-postgres-support")]
189fn impl_tokio_from_borrowed_row(t: &mut Tokens, struct_ident: &Ident, fields: &Fields) {
190    t.append(format!("
191impl<'a> From<&'a ::tokio_postgres::rows::Row> for {struct_name} {{
192    fn from(row: &'a ::tokio_postgres::rows::Row) -> Self {{
193        Self {{", struct_name=struct_ident));
194
195    for field in fields {
196        let ident = field.ident.clone().expect("Expected structfield identifier");
197
198        t.append(format!("
199            {0}: row.get(\"{0}\"),", ident));
200    }
201
202    t.append("
203        }
204    }
205}");
206}
207
208
209#[cfg(all(feature = "tokio-postgres-support", feature = "postgres-mapper"))]
210fn impl_tokio_postgres_mapper(
211    t: &mut Tokens,
212    struct_ident: &Ident,
213    fields: &Fields,
214    table_name: &str,
215) {
216    t.append(format!("
217impl ::postgres_mapper::FromTokioPostgresRow for {struct_name} {{
218    fn from_tokio_postgres_row(row: ::tokio_postgres::rows::Row)
219        -> Result<Self, ::postgres_mapper::Error> {{
220        Ok(Self {{", struct_name=struct_ident));
221
222    for field in fields {
223        let ident = field.ident.clone().expect("Expected structfield identifier");
224
225        t.append(format!("
226            {0}: row.try_get(\"{0}\")?.ok_or_else(|| ::postgres_mapper::Error::ColumnNotFound)?,", ident));
227    }
228
229    t.append("
230        })
231    }
232
233    fn from_tokio_postgres_row_ref(row: &::tokio_postgres::rows::Row)
234        -> Result<Self, ::postgres_mapper::Error> {
235        Ok(Self {");
236
237    for field in fields {
238        let ident = field.ident.clone().expect("Expected structfield identifier");
239
240        t.append(format!("
241            {0}: row.try_get(\"{0}\")?.ok_or_else(|| ::postgres_mapper::Error::ColumnNotFound)?,", ident));
242    }
243
244    t.append("
245        })
246    }");
247
248    t.append(format!(
249    "fn sql_table() -> String {{
250        \" {0} \".to_string()
251    }}"
252    , table_name));
253
254    t.append(
255    format!(
256    "fn sql_fields() -> String {{")
257    );
258
259    let field_name = fields.iter().map(|field| {
260        let ident = field.ident.clone().expect("Expected structfield identifier");
261        format!("{0}.{1}", table_name, ident)
262    }).collect::<Vec<String>>().join(", ");
263
264    t.append(format!("\" {0} \".to_string()", field_name));
265
266    t.append(
267    "}"
268    );
269
270    t.append("
271}");
272}
273
274fn get_mapper_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
275    if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "pg_mapper" {
276        match attr.interpret_meta() {
277            Some(List(ref meta)) => Some(meta.nested.iter().cloned().collect()),
278            _ => {
279                panic!("declare table name: #[pg_mapper(table = \"foo\")]");
280            }
281        }
282    } else {
283        None
284    }
285}
286
287fn get_lit_str<'a>(
288    attr_name: &Ident,
289    meta_item_name: &Ident,
290    lit: &'a syn::Lit,
291) -> Result<&'a syn::LitStr, ()> {
292    if let syn::Lit::Str(ref lit) = *lit {
293        Ok(lit)
294    } else {
295        panic!(format!(
296            "expected pg_mapper {} attribute to be a string: `{} = \"...\"`",
297            attr_name, meta_item_name
298        ));
299        #[allow(unreachable_code)]
300        Err(())
301    }
302}
303
304fn parse_table_attr(ast: &DeriveInput) -> String {
305    // Parse `#[pg_mapper(table = "foo")]`
306    let mut table_name: Option<String> = None;
307
308    for meta_items in ast.attrs.iter().filter_map(get_mapper_meta_items) {
309
310        for meta_item in meta_items {
311            match meta_item {
312                // Parse `#[pg_mapper(table = "foo")]`
313                Meta(NameValue(ref m)) if m.ident == "table" => {
314                    if let Ok(s) = get_lit_str(&m.ident, &m.ident, &m.lit) {
315                        table_name = Some(s.value());
316                    }
317                }
318
319                Meta(ref meta_item) => {
320                    panic!(format!(
321                        "unknown pg_mapper container attribute `{}`",
322                        meta_item.name()
323                    ))
324                }
325
326                Literal(_) => {
327                    panic!("unexpected literal in pg_mapper container attribute");
328                }
329            }
330        }
331    }
332
333    table_name.expect("declare table name: #[pg_mapper(table = \"foo\")]")
334}
335