const-frac 0.0.3

Types for supporting floating point constants.
Documentation
use crate::Frac;
use syn:: {
    Expr, ExprPath, Lit, Token,
    parse:: { self, Parse, ParseStream },
    punctuated::Punctuated,
};
use quote:: { quote, ToTokens };
use proc_macro2::TokenStream;

pub struct ClonedPath(pub ExprPath);

impl AsRef<ExprPath> for ClonedPath {
    fn as_ref(&self) -> &ExprPath {
        &self.0
    }
}

pub struct TokenFrac<T>
where
    T: AsRef<ExprPath>
{
    pub frac: Frac,
    pub path: Option<T>,
}

impl Parse for TokenFrac<ClonedPath> {
    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: path.map(|path| ClonedPath(path)),
        })
    }
}

impl<T> ToTokens for TokenFrac<T>
where
    T: AsRef<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.as_ref();

                quote! {
                    #path::Frac::new(#neg, #numer, #denom, #exp2, #exp5, #exp_pi)
                }
            },
            None => quote! {
                Frac::new(#neg, #numer, #denom, #exp2, #exp5, #exp_pi)
            },
        });
    }
}