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