use std::f64;
use syn::{ Attribute, Meta, NestedMeta, MetaNameValue, Lit };
use error::{ Error, Result };
fn meta(attrs: &[Attribute], name: &str, key: &str) -> Option<Meta> {
attrs.iter().filter_map(|attr| {
let meta_list = match attr.interpret_meta()? {
Meta::List(list) => {
if list.ident == name {
list
} else {
return None;
}
},
_ => return None,
};
meta_list.nested.into_iter().filter_map(|nested_meta| {
let meta = match nested_meta {
NestedMeta::Meta(meta) => meta,
_ => return None,
};
let ident = match meta.clone() {
Meta::Word(ident) => ident,
Meta::List(list) => list.ident,
Meta::NameValue(name_value) => name_value.ident,
};
if ident == key {
Some(meta)
} else {
None
}
})
.next()
})
.next()
}
fn meta_name_value(attrs: &[Attribute], name: &str, key: &str) -> Result<Option<MetaNameValue>> {
match meta(attrs, name, key) {
Some(Meta::NameValue(name_value)) => Ok(Some(name_value)),
Some(_) => {
let msg = format!("attribute must have form `#[{}({} = \"...\")]`", name, key);
Err(Error::new(msg))
},
None => Ok(None),
}
}
fn has_meta_word(attrs: &[Attribute], name: &str, key: &str) -> Result<bool> {
match meta(attrs, name, key) {
Some(Meta::Word(_)) => Ok(true),
Some(_) => {
let msg = format!("attribute must have form `#[{}({})]`", name, key);
Err(Error::new(msg))
},
None => Ok(false),
}
}
pub fn magnet_meta_name_value(attrs: &[Attribute], key: &str) -> Result<Option<MetaNameValue>> {
meta_name_value(attrs, "magnet", key)
}
pub fn serde_meta_name_value(attrs: &[Attribute], key: &str) -> Result<Option<MetaNameValue>> {
meta_name_value(attrs, "serde", key)
}
pub fn has_serde_meta_word(attrs: &[Attribute], key: &str) -> Result<bool> {
has_meta_word(attrs, "serde", key)
}
pub fn meta_value_as_str(nv: &MetaNameValue) -> Result<String> {
match nv.lit {
Lit::Str(ref string) => Ok(string.value()),
Lit::ByteStr(ref string) => String::from_utf8(string.value()).map_err(Into::into),
_ => Err(Error::new("attribute value must be a valid UTF-8 string")),
}
}
pub fn meta_value_as_num(nv: &MetaNameValue) -> Result<f64> {
match nv.lit {
Lit::Float(ref lit) => Ok(lit.value()),
Lit::Int(ref lit) => {
let value = lit.value();
let max_exact = 1 << f64::MANTISSA_DIGITS;
if value < max_exact {
Ok(value as f64)
} else {
Err(Error::new("Integer can't be exactly represented by `f64`"))
}
},
Lit::Str(ref string) => string.value().parse().map_err(Into::into),
Lit::ByteStr(ref string) => {
String::from_utf8(string.value())
.map_err(Into::into)
.and_then(|s| s.parse().map_err(Into::into))
},
_ => Err(Error::new("attribute value must be a number")),
}
}