postgres_mapper_derive/
lib.rs1extern 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 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 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