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
#![warn(clippy::all, clippy::cargo)]
#![allow(clippy::multiple_crate_versions)]
extern crate proc_macro;
mod extraction;
mod field_spec;
use field_spec::FieldSpec;
use proc_macro2::TokenStream;
use quote::quote;
use std::convert::TryFrom;
use syn::parse_macro_input;
use syn::Fields;
use syn::Ident;
use syn::Item;
use syn::ItemStruct;
#[proc_macro_attribute]
pub fn attr_args(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input_struct = if let Item::Struct(struct_data) = parse_macro_input!(input as Item) {
struct_data
} else {
panic!("The attribute can only be applied to structs")
};
let fields = extract_fields(&input_struct);
let output = impl_parse(&input_struct.ident, &fields);
(quote! {
#input_struct
#output
})
.into()
}
fn extract_fields(input_struct: &ItemStruct) -> Vec<FieldSpec> {
if let Fields::Named(fields) = &input_struct.fields {
fields
.named
.iter()
.map(TryFrom::try_from)
.map(Result::unwrap)
.collect()
} else {
panic!("The attribute can only be applied to structs with named fields")
}
}
fn impl_parse(struct_name: &Ident, fields: &[FieldSpec]) -> TokenStream {
let extractors = fields.iter().map(extraction::build_extractor);
let extraction = quote! {
#(#extractors)*
};
let idents = fields.iter().map(FieldSpec::ident);
let struct_return = quote! {
Ok(#struct_name {
#(#idents),*
})
};
quote! {
impl syn::parse::Parse for #struct_name {
fn parse(buffer: &syn::parse::ParseBuffer) -> syn::parse::Result<Self> {
use std::convert::TryInto;
let mut attr_args = attribution::Parameters::parse(buffer)?;
#extraction
#struct_return
}
}
}
}