1 use quote::{quote, quote_spanned, ToTokens, TokenStreamExt}; 2 use syn::spanned::Spanned; 3 4 use crate::options::{AttrsField, ForwardAttrsFilter}; 5 6 #[derive(Default)] 7 pub struct ForwardAttrs<'a> { 8 pub filter: Option<&'a ForwardAttrsFilter>, 9 pub field: Option<&'a AttrsField>, 10 } 11 12 impl ForwardAttrs<'_> { 13 /// Check if this will forward any attributes; this requires both that 14 /// there be a filter which can match some attributes and a field to receive them. will_forward_any(&self) -> bool15 pub fn will_forward_any(&self) -> bool { 16 if let Some(filter) = self.filter { 17 !filter.is_empty() && self.field.is_some() 18 } else { 19 false 20 } 21 } 22 23 /// Get the field declarations to support attribute forwarding as_declaration(&self) -> Option<Declaration<'_>>24 pub fn as_declaration(&self) -> Option<Declaration<'_>> { 25 self.field.map(Declaration) 26 } 27 28 /// Get the match arms for attribute matching as_match_arms(&self) -> MatchArms<'_>29 pub fn as_match_arms(&self) -> MatchArms<'_> { 30 MatchArms(self) 31 } 32 33 /// Get the statement that will try to transform forwarded attributes into 34 /// the result expected by the receiver field. as_value_populator(&self) -> Option<ValuePopulator<'_>>35 pub fn as_value_populator(&self) -> Option<ValuePopulator<'_>> { 36 self.field.map(ValuePopulator) 37 } 38 39 /// Get the field initializer for use when building the deriving struct. as_initializer(&self) -> Option<Initializer<'_>>40 pub fn as_initializer(&self) -> Option<Initializer<'_>> { 41 self.field.map(Initializer) 42 } 43 } 44 45 pub struct Declaration<'a>(pub &'a AttrsField); 46 47 impl ToTokens for Declaration<'_> { to_tokens(&self, tokens: &mut proc_macro2::TokenStream)48 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 49 let ident = &self.0.ident; 50 tokens.append_all(quote! { 51 let mut __fwd_attrs: ::darling::export::Vec<::darling::export::syn::Attribute> = vec![]; 52 let mut #ident: ::darling::export::Option<_> = None; 53 }); 54 } 55 } 56 57 pub struct ValuePopulator<'a>(pub &'a AttrsField); 58 59 impl ToTokens for ValuePopulator<'_> { to_tokens(&self, tokens: &mut proc_macro2::TokenStream)60 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 61 let AttrsField { ident, with } = self.0; 62 let initializer_expr = match with { 63 Some(with) => quote_spanned!(with.span()=> __errors.handle(#with(__fwd_attrs))), 64 None => quote!(::darling::export::Some(__fwd_attrs)), 65 }; 66 tokens.append_all(quote!(#ident = #initializer_expr;)); 67 } 68 } 69 70 pub struct Initializer<'a>(pub &'a AttrsField); 71 72 impl ToTokens for Initializer<'_> { to_tokens(&self, tokens: &mut proc_macro2::TokenStream)73 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 74 let ident = &self.0.ident; 75 tokens.append_all(quote!(#ident: #ident.expect("Errors were already checked"),)); 76 } 77 } 78 79 pub struct MatchArms<'a>(&'a ForwardAttrs<'a>); 80 81 impl ToTokens for MatchArms<'_> { to_tokens(&self, tokens: &mut proc_macro2::TokenStream)82 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 83 if !self.0.will_forward_any() { 84 tokens.append_all(quote!(_ => continue)); 85 return; 86 } 87 88 let push_command = quote!(__fwd_attrs.push(__attr.clone())); 89 90 tokens.append_all( 91 match self 92 .0 93 .filter 94 .expect("Can only forward attributes if filter is defined") 95 { 96 ForwardAttrsFilter::All => quote!(_ => #push_command), 97 ForwardAttrsFilter::Only(idents) => { 98 let names = idents.to_strings(); 99 quote! { 100 #(#names)|* => #push_command, 101 _ => continue, 102 } 103 } 104 }, 105 ); 106 } 107 } 108