1 use proc_macro2::TokenStream;
2 use quote::{quote, ToTokens};
3 
4 use crate::util::PathList;
5 
6 use super::ForwardAttrs;
7 
8 /// Infrastructure for generating an attribute extractor.
9 pub trait ExtractAttribute {
10     /// A set of mutable declarations for all members of the implementing type.
local_declarations(&self) -> TokenStream11     fn local_declarations(&self) -> TokenStream;
12 
13     /// Gets the list of attribute names that should be parsed by the extractor.
attr_names(&self) -> &PathList14     fn attr_names(&self) -> &PathList;
15 
forward_attrs(&self) -> &ForwardAttrs<'_>16     fn forward_attrs(&self) -> &ForwardAttrs<'_>;
17 
18     /// Gets the name used by the generated impl to return to the `syn` item passed as input.
param_name(&self) -> TokenStream19     fn param_name(&self) -> TokenStream;
20 
21     /// Get the tokens to access a borrowed list of attributes where extraction will take place.
22     ///
23     /// By default, this will be `&#input.attrs` where `#input` is `self.param_name()`.
attrs_accessor(&self) -> TokenStream24     fn attrs_accessor(&self) -> TokenStream {
25         let input = self.param_name();
26         quote!(&#input.attrs)
27     }
28 
29     /// Gets the core from-meta-item loop that should be used on matching attributes.
core_loop(&self) -> TokenStream30     fn core_loop(&self) -> TokenStream;
31 
32     /// Generates the main extraction loop.
extractor(&self) -> TokenStream33     fn extractor(&self) -> TokenStream {
34         let mut declarations = self.local_declarations();
35         self.forward_attrs()
36             .as_declaration()
37             .to_tokens(&mut declarations);
38 
39         let will_parse_any = !self.attr_names().is_empty();
40 
41         // Forwarding requires both that there be some items we would forward,
42         // and a place that will keep the forwarded items.
43         let will_fwd_any = self.forward_attrs().will_forward_any();
44 
45         if !(will_parse_any || will_fwd_any) {
46             return quote! {
47                 #declarations
48             };
49         }
50 
51         let attrs_accessor = self.attrs_accessor();
52 
53         // The block for parsing attributes whose names have been claimed by the target
54         // struct. If no attributes were claimed, this is a pass-through.
55         let parse_handled = if will_parse_any {
56             let attr_names = self.attr_names().to_strings();
57             let core_loop = self.core_loop();
58             quote!(
59                 #(#attr_names)|* => {
60                     match ::darling::util::parse_attribute_to_meta_list(__attr) {
61                         ::darling::export::Ok(__data) => {
62                             match ::darling::export::NestedMeta::parse_meta_list(__data.tokens) {
63                                 ::darling::export::Ok(ref __items) => {
64                                     if __items.is_empty() {
65                                         continue;
66                                     }
67 
68                                     #core_loop
69                                 }
70                                 ::darling::export::Err(__err) => {
71                                     __errors.push(__err.into());
72                                 }
73                             }
74                         }
75                         // darling was asked to handle this attribute name, but the actual attribute
76                         // isn't one that darling can work with. This either indicates a typing error
77                         // or some misunderstanding of the meta attribute syntax; in either case, the
78                         // caller should get a useful error.
79                         ::darling::export::Err(__err) => {
80                             __errors.push(__err);
81                         }
82                     }
83                 }
84             )
85         } else {
86             quote!()
87         };
88 
89         let fwd_population = self.forward_attrs().as_value_populator();
90 
91         // Specifies the behavior for unhandled attributes. They will either be silently ignored or
92         // forwarded to the inner struct for later analysis.
93         let forward_unhandled = self.forward_attrs().as_match_arms();
94 
95         quote!(
96             #declarations
97             use ::darling::ToTokens;
98 
99             for __attr in #attrs_accessor {
100                 // Filter attributes based on name
101                 match ::darling::export::ToString::to_string(&__attr.path().clone().into_token_stream()).as_str() {
102                     #parse_handled
103                     #forward_unhandled
104                 }
105             }
106 
107             #fwd_population
108         )
109     }
110 }
111