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 proc_macro2::TokenStream;
use crate::options::ForwardAttrs;
use crate::util::PathList;
/// Infrastructure for generating an attribute extractor.
pub trait ExtractAttribute {
/// A set of mutable declarations for all members of the implementing type.
fn local_declarations(&self) -> TokenStream;
/// A set of immutable declarations for all members of the implementing type.
/// This is used in the case where a deriving struct handles no attributes and therefore can
/// never change its default state.
fn immutable_declarations(&self) -> TokenStream;
/// Gets the list of attribute names that should be parsed by the extractor.
fn attr_names(&self) -> &PathList;
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) -> TokenStream;
/// Get the tokens to access a borrowed list of attributes where extraction will take place.
///
/// By default, this will be `&#input.attrs` where `#input` is `self.param_name()`.
fn attrs_accessor(&self) -> TokenStream {
let input = self.param_name();
quote!(&#input.attrs)
}
/// Gets the core from-meta-item loop that should be used on matching attributes.
fn core_loop(&self) -> TokenStream;
fn declarations(&self) -> TokenStream {
if !self.attr_names().is_empty() {
self.local_declarations()
} else {
self.immutable_declarations()
}
}
/// Generates the main extraction loop.
fn extractor(&self) -> TokenStream {
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 attrs_accessor = self.attrs_accessor();
// 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().to_strings();
let core_loop = self.core_loop();
quote!(
#(#attr_names)|* => {
match ::darling::util::parse_attribute_to_meta_list(__attr) {
::darling::export::Ok(__data) => {
if __data.nested.is_empty() {
continue;
}
let __items = &__data.nested;
#core_loop
}
// darling was asked to handle this attribute name, but the actual attribute
// isn't one that darling can work with. This either indicates a typing error
// or some misunderstanding of the meta attribute syntax; in either case, the
// caller should get a useful error.
::darling::export::Err(__err) => {
__errors.push(__err);
}
}
}
)
} 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
use ::darling::ToTokens;
let mut __fwd_attrs: ::darling::export::Vec<::syn::Attribute> = vec![];
for __attr in #attrs_accessor {
// Filter attributes based on name
match ::darling::export::ToString::to_string(&__attr.path.clone().into_token_stream()).as_str() {
#parse_handled
#forward_unhandled
}
}
)
}
}
fn forwards_to_local(behavior: &ForwardAttrs) -> TokenStream {
let push_command = quote!(__fwd_attrs.push(__attr.clone()));
match *behavior {
ForwardAttrs::All => quote!(_ => #push_command),
ForwardAttrs::Only(ref idents) => {
let names = idents.to_strings();
quote!(
#(#names)|* => #push_command,
_ => continue,
)
}
}
}