use crate::Frac;
use syn:: {
Expr, ExprPath, Lit, Token,
parse:: { self, Parse, ParseStream },
punctuated::Punctuated,
};
use quote:: { quote, ToTokens };
use proc_macro2::TokenStream;
use std::borrow::Borrow;
pub struct TokenFrac<T>
where
T: Borrow<ExprPath>
{
pub frac: Frac,
pub path: Option<T>,
}
impl Parse for TokenFrac<ExprPath> {
fn parse(input: ParseStream) -> parse::Result<Self> {
use Lit::*;
let mut it = Punctuated::<Expr, Token![,]>::parse_separated_nonempty(input)?.into_iter();
let path = match it.next() {
Some(Expr::Path(ref expr)) => expr.clone().into(),
Some(Expr::Group(ref expr)) => match &*expr.expr {
&Expr::Path(ref expr) => expr.clone().into(),
_ => return Err(input.error(format!("{:?}", expr))),
},
Some(expr) => return Err(input.error(format!("{:?}", expr))),
None => None,
};
let (lit, neg) = match it.next() {
Some(Expr::Lit(ref expr)) => (expr.lit.clone(), false),
Some(Expr::Group(ref expr)) => match &*expr.expr {
&Expr::Lit(ref expr) => (expr.lit.clone(), false),
_ => return Err(input.error(format!("{:?}", expr))),
},
Some(Expr::Unary(ref expr)) => match &*expr.expr {
&Expr::Lit(ref expr) => (expr.lit.clone(), true),
_ => return Err(input.error(format!("{:?}", expr))),
},
Some(expr) => return Err(input.error(format!("{:?}", expr))),
None => return Err(input.error("No value specified.")),
};
let mut s = match lit {
Str(s) => s.value(),
ByteStr(s) => String::from_utf8(s.value()).unwrap(),
Byte(s) => {
let ch: char = s.value().into();
let mut s = String::new();
s.extend(Some(ch));
s
},
Char(ch) => {
let mut s = String::new();
s.extend(Some(ch.value()));
s
},
Int(s) => s.to_string(),
Float(s) => s.to_string(),
Bool(s) => if s.value() { "1".to_string() } else { "0".to_string() },
Verbatim(s) => s.to_string(),
};
if neg {
s.insert(0, '-');
}
Ok(Self {
frac: s.parse().map_err(|_| input.error("can't parse"))?,
path,
})
}
}
impl<T> ToTokens for TokenFrac<T>
where
T: Borrow<ExprPath>
{
fn to_tokens(&self, tokens: &mut TokenStream) {
let neg = self.frac.is_negative();
let numer = self.frac.numer();
let denom = self.frac.denom();
let exp2 = self.frac.exp2();
let exp5 = self.frac.exp5();
let exp_pi = self.frac.exp_pi();
tokens.extend(match self.path {
Some(ref path) => {
let path = path.borrow();
quote! {
#path::Frac::new(#neg, #numer, #denom, #exp2, #exp5, #exp_pi)
}
},
None => quote! {
Frac::new(#neg, #numer, #denom, #exp2, #exp5, #exp_pi)
},
});
}
}