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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use proc_macro2::TokenStream;

use crate::options::ForwardAttrs;
use crate::util::PathList;

/// Infrastructure for generating an attribute extractor.
pub trait ExtractAttribute {
    /// A set of mutable declarations for all members of the implementing type.
    fn local_declarations(&self) -> TokenStream;

    /// A set of immutable declarations for all members of the implementing type.
    /// This is used in the case where a deriving struct handles no attributes and therefore can
    /// never change its default state.
    fn immutable_declarations(&self) -> TokenStream;

    /// Gets the list of attribute names that should be parsed by the extractor.
    fn attr_names(&self) -> &PathList;

    fn forwarded_attrs(&self) -> Option<&ForwardAttrs>;

    /// Gets the name used by the generated impl to return to the `syn` item passed as input.
    fn param_name(&self) -> TokenStream;

    /// Get the tokens to access a borrowed list of attributes where extraction will take place.
    ///
    /// By default, this will be `&#input.attrs` where `#input` is `self.param_name()`.
    fn attrs_accessor(&self) -> TokenStream {
        let input = self.param_name();
        quote!(&#input.attrs)
    }

    /// Gets the core from-meta-item loop that should be used on matching attributes.
    fn core_loop(&self) -> TokenStream;

    fn declarations(&self) -> TokenStream {
        if !self.attr_names().is_empty() {
            self.local_declarations()
        } else {
            self.immutable_declarations()
        }
    }

    /// Generates the main extraction loop.
    fn extractor(&self) -> TokenStream {
        let declarations = self.declarations();

        let will_parse_any = !self.attr_names().is_empty();
        let will_fwd_any = self
            .forwarded_attrs()
            .map(|fa| !fa.is_empty())
            .unwrap_or_default();

        if !(will_parse_any || will_fwd_any) {
            return quote! {
                #declarations
            };
        }

        let attrs_accessor = self.attrs_accessor();

        // The block for parsing attributes whose names have been claimed by the target
        // struct. If no attributes were claimed, this is a pass-through.
        let parse_handled = if will_parse_any {
            let attr_names = self.attr_names().to_strings();
            let core_loop = self.core_loop();
            quote!(
                #(#attr_names)|* => {
                    match ::darling::util::parse_attribute_to_meta_list(__attr) {
                        ::darling::export::Ok(__data) => {
                            if __data.nested.is_empty() {
                                continue;
                            }

                            let __items = &__data.nested;

                            #core_loop
                        }
                        // darling was asked to handle this attribute name, but the actual attribute
                        // isn't one that darling can work with. This either indicates a typing error
                        // or some misunderstanding of the meta attribute syntax; in either case, the
                        // caller should get a useful error.
                        ::darling::export::Err(__err) => {
                            __errors.push(__err);
                        }
                    }
                }
            )
        } else {
            quote!()
        };

        // Specifies the behavior for unhandled attributes. They will either be silently ignored or
        // forwarded to the inner struct for later analysis.
        let forward_unhandled = if will_fwd_any {
            forwards_to_local(self.forwarded_attrs().unwrap())
        } else {
            quote!(_ => continue)
        };

        quote!(
            #declarations
            use ::darling::ToTokens;
            let mut __fwd_attrs: ::darling::export::Vec<::syn::Attribute> = vec![];

            for __attr in #attrs_accessor {
                // Filter attributes based on name
                match  ::darling::export::ToString::to_string(&__attr.path.clone().into_token_stream()).as_str() {
                    #parse_handled
                    #forward_unhandled
                }
            }
        )
    }
}

fn forwards_to_local(behavior: &ForwardAttrs) -> TokenStream {
    let push_command = quote!(__fwd_attrs.push(__attr.clone()));
    match *behavior {
        ForwardAttrs::All => quote!(_ => #push_command),
        ForwardAttrs::Only(ref idents) => {
            let names = idents.to_strings();
            quote!(
                #(#names)|* => #push_command,
                _ => continue,
            )
        }
    }
}