1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use quote::Tokens;

mod default_expr;
mod error;
mod field;
mod from_meta_impl;
mod from_derive_impl;
mod from_field;
mod from_type_param;
mod from_variant_impl;
mod outer_from_impl;
mod trait_impl;
mod variant;
mod variant_data;

pub use self::default_expr::DefaultExpression;
pub use self::field::Field;
pub use self::from_meta_impl::FromMetaImpl;
pub use self::from_derive_impl::FromDeriveInputImpl;
pub use self::from_field::FromFieldImpl;
pub use self::from_type_param::FromTypeParamImpl;
pub use self::from_variant_impl::FromVariantImpl;
pub use self::outer_from_impl::OuterFromImpl;
pub use self::trait_impl::TraitImpl;
pub use self::variant::Variant;
pub use self::variant_data::FieldsGen;

use options::ForwardAttrs;

/// Infrastructure for generating an attribute extractor.
pub trait ExtractAttribute {
    fn local_declarations(&self) -> Tokens;

    fn immutable_declarations(&self) -> Tokens;

    /// Gets the list of attribute names that should be parsed by the extractor.
    fn attr_names(&self) -> &[&str];

    fn forwarded_attrs(&self) -> Option<&ForwardAttrs>;

    /// Gets the name used by the generated impl to return to the `syn` item passed as input.
    fn param_name(&self) -> Tokens;

    /// Gets the core from-meta-item loop that should be used on matching attributes.
    fn core_loop(&self) -> Tokens;

    fn declarations(&self) -> Tokens {
        if !self.attr_names().is_empty() {
            self.local_declarations()
        } else {
            self.immutable_declarations()
        }
    }

    /// Generates the main extraction loop.
    fn extractor(&self) -> Tokens {
        let declarations = self.declarations();

        let will_parse_any = !self.attr_names().is_empty();
        let will_fwd_any = self.forwarded_attrs()
            .map(|fa| !fa.is_empty())
            .unwrap_or_default();

        if !(will_parse_any || will_fwd_any) {
            return quote! {
                #declarations
            };
        }

        let input = self.param_name();

        // The block for parsing attributes whose names have been claimed by the target
        // struct. If no attributes were claimed, this is a pass-through.
        let parse_handled = if will_parse_any {
            let attr_names = self.attr_names();
            let core_loop = self.core_loop();
            quote!(
                #(#attr_names)|* => {
                    if let Some(::syn::Meta::List(ref __data)) = __attr.interpret_meta() {
                        let __items = &__data.nested;

                        #core_loop
                    } else {
                        // darling currently only supports list-style
                        continue
                    }
                }
            )
        } else {
            quote!()
        };

        // Specifies the behavior for unhandled attributes. They will either be silently ignored or
        // forwarded to the inner struct for later analysis.
        let forward_unhandled = if will_fwd_any {
            forwards_to_local(self.forwarded_attrs().unwrap())
        } else {
            quote!(_ => continue)
        };

        quote!(
            #declarations
            let mut __fwd_attrs: ::darling::export::Vec<::syn::Attribute> = vec![];

            for __attr in &#input.attrs {
                // Filter attributes based on name
                match __attr.path.segments.iter().map(|s| s.ident.as_ref()).collect::<Vec<&str>>().join("::").as_str() {
                    #parse_handled
                    #forward_unhandled
                }
            }
        )
    }
}

fn forwards_to_local(behavior: &ForwardAttrs) -> Tokens {
    let push_command = quote!(__fwd_attrs.push(__attr.clone()));
    match *behavior {
        ForwardAttrs::All => quote!(_ => #push_command),
        ForwardAttrs::Only(ref idents) => {
            let names = idents.as_strs();
            quote!(
                #(#names)|* => #push_command,
                _ => continue,
            )
        }
    }
}