rational_extensions 0.4.7

Extensions for rational numbers.
Documentation
use crate::{Ratio, try_from_str};
use core::fmt::{self, Formatter};
use core::marker::PhantomData;
use core::ops::Mul;
use core::str::FromStr;
use num_integer::Integer;
use num_traits::Pow;
use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor};
/// Wrapper around a `num_rational::Ratio` that
/// deserializes a JSON string representing a rational number in
/// fractional or decimal notation to a Ratio<T>.
#[derive(Clone, Copy, Debug)]
pub struct Rational<T>(pub Ratio<T>);
impl<'de, T> Deserialize<'de> for Rational<T>
where
    T: Clone
        + From<u8>
        + FromStr
        + Integer
        + for<'a> Mul<&'a T, Output = T>
        + Pow<usize, Output = T>,
{
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        /// Visitor used to deserialize a JSON string into a Rational.
        struct RationalVisitor<T> {
            /// Does not own nor drop a `T`.
            _x: PhantomData<fn() -> T>,
        }
        impl<T> Visitor<'_> for RationalVisitor<T>
        where
            T: Clone
                + From<u8>
                + FromStr
                + Integer
                + for<'a> Mul<&'a T, Output = T>
                + Pow<usize, Output = T>,
        {
            type Value = Rational<T>;
            fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
                formatter.write_str("struct Rational")
            }
            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                try_from_str(v).map_or_else(
                    |_| {
                        Err(E::invalid_value(
                            Unexpected::Str(v),
                            &"a rational number in fraction or decimal notation",
                        ))
                    },
                    |val| Ok(Rational(val)),
                )
            }
        }
        deserializer.deserialize_str(RationalVisitor { _x: PhantomData })
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_serde() -> Result<(), serde_json::Error> {
        assert_eq!(
            Ratio::new(2u8, 3u8),
            serde_json::from_str::<Rational::<u8>>(r#""2/3""#)?.0
        );
        assert_eq!(
            Ratio::new(67u8, 100u8),
            serde_json::from_str::<Rational::<u8>>(r#""0.67""#)?.0
        );
        Ok(())
    }
}