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) -> TokenStream116fn 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