use syn::{Meta, NestedMeta, Lit, Attribute, Variant, Field};
use proc_macro2::{TokenStream, Span};
use std::str::FromStr;
fn find_meta_item<'a, F, R, I>(itr: I, pred: F) -> Option<R> where
F: FnMut(&NestedMeta) -> Option<R> + Clone,
I: Iterator<Item=&'a Attribute>
{
itr.filter_map(|attr| {
let pair = attr.path.segments.first()?;
let seg = pair.value();
if seg.ident == "codec" {
let meta = attr.interpret_meta();
if let Some(Meta::List(ref meta_list)) = meta {
return meta_list.nested.iter().filter_map(pred.clone()).next();
}
}
None
}).next()
}
pub fn index(v: &Variant, i: usize) -> TokenStream {
let index = find_meta_item(v.attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta {
if nv.ident == "index" {
if let Lit::Str(ref s) = nv.lit {
let byte: u8 = s.value().parse().expect("Numeric index expected.");
return Some(byte)
}
}
}
None
});
index.map(|i| quote! { #i })
.unwrap_or_else(|| v.discriminant
.as_ref()
.map(|&(_, ref expr)| quote! { #expr })
.unwrap_or_else(|| quote! { #i })
)
}
pub fn get_encoded_as_type(field_entry: &Field) -> Option<TokenStream> {
find_meta_item(field_entry.attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta {
if nv.ident == "encoded_as"{
if let Lit::Str(ref s) = nv.lit {
return Some(
TokenStream::from_str(&s.value())
.expect("`encoded_as` should be a valid rust type!")
);
}
}
}
None
})
}
pub fn get_enable_compact(field_entry: &Field) -> bool {
find_meta_item(field_entry.attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::Word(ref word)) = meta {
if word == "compact" {
return Some(());
}
}
None
}).is_some()
}
pub fn get_skip(attrs: &Vec<Attribute>) -> Option<Span> {
find_meta_item(attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::Word(ref word)) = meta {
if word == "skip" {
return Some(word.span());
}
}
None
})
}
pub fn filter_skip_named<'a>(fields: &'a syn::FieldsNamed) -> impl Iterator<Item=&Field> + 'a {
fields.named.iter()
.filter(|f| get_skip(&f.attrs).is_none())
}
pub fn filter_skip_unnamed<'a>(fields: &'a syn::FieldsUnnamed) -> impl Iterator<Item=(usize, &Field)> + 'a {
fields.unnamed.iter()
.enumerate()
.filter(|(_, f)| get_skip(&f.attrs).is_none())
}