use syn::parse;
use std::convert::TryInto;
use syn::ext::IdentExt;
pub trait ParseOpt: Sized {
fn parse(input: parse::ParseStream) -> parse::Result<Option<Self>>;
}
pub trait ParseOptExt {
fn parse_opt<T: ParseOpt>(&self) -> parse::Result<Option<T>>;
}
impl<'a> ParseOptExt for parse::ParseBuffer<'a> {
fn parse_opt<T: ParseOpt>(&self) -> parse::Result<Option<T>> {
T::parse(self)
}
}
pub fn eat_pseudo_keyword(input: parse::ParseStream, kw: &str) -> bool {
input.step(|cursor| {
if let Some((ident, rest)) = cursor.ident() {
if ident == kw {
return Ok(((), rest));
}
}
Err(cursor.error("expected identifier"))
}).is_ok()
}
pub fn parse_ident_or_rust_keyword(input: parse::ParseStream) -> parse::Result<syn::Ident> {
syn::Ident::parse_any(input)
}
pub fn as_ident(expr: &syn::Expr) -> Option<&syn::Ident> {
let path = match *expr {
syn::Expr::Path(syn::ExprPath {ref path, qself: None, ..}) => path,
_ => return None
};
path.get_ident()
}
pub fn as_lit(expr: &syn::Expr) -> Option<&syn::Lit> {
let mut inner = expr;
while let syn::Expr::Group(syn::ExprGroup { expr, .. }) = inner {
inner = expr;
}
match inner {
syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some(lit),
_ => None
}
}
pub fn as_lit_with_negation(expr: &syn::Expr) -> Option<(&syn::Lit, bool)> {
let mut inner = expr;
while let syn::Expr::Group(syn::ExprGroup { expr, .. }) = inner {
inner = expr;
}
match inner {
syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some((lit, false)),
syn::Expr::Unary(syn::ExprUnary { op: syn::UnOp::Neg(_), ref expr, .. } ) => {
match &**expr {
syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some((lit, true)),
_ => None
}
}
_ => None
}
}
pub fn as_unsigned_number(expr: &syn::Expr) -> Option<u64> {
match as_lit(expr)? {
syn::Lit::Int(i) => i.base10_parse().ok(),
_ => None
}
}
pub fn as_signed_number(expr: &syn::Expr) -> Option<i64> {
let (expr, negated) = as_lit_with_negation(expr)?;
match expr {
syn::Lit::Int(i) => if let Ok(value) = i.base10_parse::<u64>() {
let value: i64 = value.try_into().ok()?;
Some (if negated {-value} else {value})
} else {
None
},
_ => None
}
}
pub fn as_float(expr: &syn::Expr) -> Option<f64> {
let (expr, negated) = as_lit_with_negation(expr)?;
match expr {
syn::Lit::Float(i) => i.base10_parse::<f64>().ok().map(|i| if negated { -i } else { i } ),
_ => None
}
}