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