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 {
    // Parse the inputs
    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
            }
        }
    }
}