1 use proc_macro2::TokenStream;
2 
3 use crate::options::ForwardAttrs;
4 use crate::util::PathList;
5 
6 /// Infrastructure for generating an attribute extractor.
7 pub trait ExtractAttribute {
8     /// A set of mutable declarations for all members of the implementing type.
local_declarations(&self) -> TokenStream9     fn local_declarations(&self) -> TokenStream;
10 
11     /// A set of immutable declarations for all members of the implementing type.
12     /// This is used in the case where a deriving struct handles no attributes and therefore can
13     /// never change its default state.
immutable_declarations(&self) -> TokenStream14     fn immutable_declarations(&self) -> TokenStream;
15 
16     /// Gets the list of attribute names that should be parsed by the extractor.
attr_names(&self) -> &PathList17     fn attr_names(&self) -> &PathList;
18 
forwarded_attrs(&self) -> Option<&ForwardAttrs>19     fn forwarded_attrs(&self) -> Option<&ForwardAttrs>;
20 
21     /// Gets the name used by the generated impl to return to the `syn` item passed as input.
param_name(&self) -> TokenStream22     fn param_name(&self) -> TokenStream;
23 
24     /// Get the tokens to access a borrowed list of attributes where extraction will take place.
25     ///
26     /// By default, this will be `&#input.attrs` where `#input` is `self.param_name()`.
attrs_accessor(&self) -> TokenStream27     fn attrs_accessor(&self) -> TokenStream {
28         let input = self.param_name();
29         quote!(&#input.attrs)
30     }
31 
32     /// Gets the core from-meta-item loop that should be used on matching attributes.
core_loop(&self) -> TokenStream33     fn core_loop(&self) -> TokenStream;
34 
declarations(&self) -> TokenStream35     fn declarations(&self) -> TokenStream {
36         if !self.attr_names().is_empty() {
37             self.local_declarations()
38         } else {
39             self.immutable_declarations()
40         }
41     }
42 
43     /// Generates the main extraction loop.
extractor(&self) -> TokenStream44     fn extractor(&self) -> TokenStream {
45         let declarations = self.declarations();
46 
47         let will_parse_any = !self.attr_names().is_empty();
48         let will_fwd_any = self
49             .forwarded_attrs()
50             .map(|fa| !fa.is_empty())
51             .unwrap_or_default();
52 
53         if !(will_parse_any || will_fwd_any) {
54             return quote! {
55                 #declarations
56             };
57         }
58 
59         let attrs_accessor = self.attrs_accessor();
60 
61         // The block for parsing attributes whose names have been claimed by the target
62         // struct. If no attributes were claimed, this is a pass-through.
63         let parse_handled = if will_parse_any {
64             let attr_names = self.attr_names().to_strings();
65             let core_loop = self.core_loop();
66             quote!(
67                 #(#attr_names)|* => {
68                     match ::darling::util::parse_attribute_to_meta_list(__attr) {
69                         ::darling::export::Ok(__data) => {
70                             if __data.nested.is_empty() {
71                                 continue;
72                             }
73 
74                             let __items = &__data.nested;
75 
76                             #core_loop
77                         }
78                         // darling was asked to handle this attribute name, but the actual attribute
79                         // isn't one that darling can work with. This either indicates a typing error
80                         // or some misunderstanding of the meta attribute syntax; in either case, the
81                         // caller should get a useful error.
82                         ::darling::export::Err(__err) => {
83                             __errors.push(__err);
84                         }
85                     }
86                 }
87             )
88         } else {
89             quote!()
90         };
91 
92         // Specifies the behavior for unhandled attributes. They will either be silently ignored or
93         // forwarded to the inner struct for later analysis.
94         let forward_unhandled = if will_fwd_any {
95             forwards_to_local(self.forwarded_attrs().unwrap())
96         } else {
97             quote!(_ => continue)
98         };
99 
100         quote!(
101             #declarations
102             use ::darling::ToTokens;
103             let mut __fwd_attrs: ::darling::export::Vec<::syn::Attribute> = vec![];
104 
105             for __attr in #attrs_accessor {
106                 // Filter attributes based on name
107                 match  ::darling::export::ToString::to_string(&__attr.path.clone().into_token_stream()).as_str() {
108                     #parse_handled
109                     #forward_unhandled
110                 }
111             }
112         )
113     }
114 }
115 
forwards_to_local(behavior: &ForwardAttrs) -> TokenStream116 fn forwards_to_local(behavior: &ForwardAttrs) -> TokenStream {
117     let push_command = quote!(__fwd_attrs.push(__attr.clone()));
118     match *behavior {
119         ForwardAttrs::All => quote!(_ => #push_command),
120         ForwardAttrs::Only(ref idents) => {
121             let names = idents.to_strings();
122             quote!(
123                 #(#names)|* => #push_command,
124                 _ => continue,
125             )
126         }
127     }
128 }
129