1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
extern crate proc_macro;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, FieldsNamed};
use crate::proc_macro::TokenStream;
use crate::attr::FieldAttribute;
use std::convert::TryInto;
mod attr;
#[proc_macro_derive(Guzzle, attributes(guzzle, no_guzzle, deep_guzzle))]
pub fn guzzle_macro_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input);
impl_guzzle(ast)
}
fn impl_guzzle(ast: DeriveInput) -> TokenStream {
match &ast.data {
Data::Struct(s) => match &s.fields {
Fields::Named(fields) => impl_guzzle_named_fields(&ast, fields),
_ => unimplemented!(),
},
_ => unimplemented!(),
}
}
fn fields_to_attributes(fields: &FieldsNamed) -> Result<Vec<FieldAttribute>, Vec<syn::Error>> {
let mut oks = vec![];
let mut errs = vec![];
fields.named.iter().for_each(|field| {
match field.try_into() {
Ok(field_attribute) => oks.push(field_attribute),
Err(error) => errs.push(error),
};
});
if !errs.is_empty() {
Err(errs)
} else {
Ok(oks)
}
}
fn handle_errors(errors: Vec<syn::Error>) -> TokenStream {
let mut output = TokenStream::new();
for error in errors.iter() {
output.extend(TokenStream::from(error.to_compile_error()));
}
output
}
fn impl_guzzle_named_fields(ast: &DeriveInput, fields: &FieldsNamed) -> TokenStream {
match fields_to_attributes(fields) {
Ok(attr) => attributes_to_generated_code(ast, attr),
Err(err) => handle_errors(err),
}
}
fn attributes_to_generated_code(ast: &DeriveInput, attributes: Vec<FieldAttribute>) -> TokenStream {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let mut deep_guzzles = vec![];
let mut keys = vec![];
let mut matchers = vec![];
let mut parsers = vec![];
for field_attribute in &attributes {
if let Some(expr) = field_attribute.get_recursion() {
deep_guzzles.push(expr);
} else {
for (key, matcher, parser) in field_attribute.get_arm_parts() {
keys.push(key);
matchers.push(matcher);
parsers.push(parser);
}
}
}
let gen = quote! {
impl #impl_generics Guzzle for #name #ty_generics #where_clause {
fn guzzle<T>(&mut self, (key, value): (T, String)) -> Option<(T, String)>
where T: AsRef<str>
{
#(
let result = self.#deep_guzzles.guzzle((key, value));
if result.is_none() { return None };
let (key, value) = result.unwrap();
)*
match key.as_ref() {
#( #matchers => self.#keys = #parsers(value), )*
_ => return Some((key, value)),
};
None
}
}
};
gen.into()
}