rational_extensions

Extends num_rational::Ratio<T>.
git clone https://git.philomathiclife.com/repos/rational_extensions
Log | Files | Refs | README

rational.rs (2471B)


      1 use crate::{Ratio, try_from_str};
      2 use core::fmt::{self, Formatter};
      3 use core::marker::PhantomData;
      4 use core::ops::Mul;
      5 use core::str::FromStr;
      6 use num_integer::Integer;
      7 use num_traits::Pow;
      8 use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor};
      9 /// Wrapper around a `num_rational::Ratio` that
     10 /// deserializes a JSON string representing a rational number in
     11 /// fractional or decimal notation to a Ratio&lt;T&gt;.
     12 #[derive(Clone, Copy, Debug)]
     13 pub struct Rational<T>(pub Ratio<T>);
     14 impl<'de, T> Deserialize<'de> for Rational<T>
     15 where
     16     T: Clone
     17         + From<u8>
     18         + FromStr
     19         + Integer
     20         + for<'a> Mul<&'a T, Output = T>
     21         + Pow<usize, Output = T>,
     22 {
     23     #[inline]
     24     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     25     where
     26         D: Deserializer<'de>,
     27     {
     28         /// Visitor used to deserialize a JSON string into a Rational.
     29         struct RationalVisitor<T> {
     30             /// Does not own nor drop a `T`.
     31             _x: PhantomData<fn() -> T>,
     32         }
     33         impl<T> Visitor<'_> for RationalVisitor<T>
     34         where
     35             T: Clone
     36                 + From<u8>
     37                 + FromStr
     38                 + Integer
     39                 + for<'a> Mul<&'a T, Output = T>
     40                 + Pow<usize, Output = T>,
     41         {
     42             type Value = Rational<T>;
     43             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
     44                 formatter.write_str("struct Rational")
     45             }
     46             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
     47             where
     48                 E: de::Error,
     49             {
     50                 try_from_str(v).map_or_else(
     51                     |_| {
     52                         Err(E::invalid_value(
     53                             Unexpected::Str(v),
     54                             &"a rational number in fraction or decimal notation",
     55                         ))
     56                     },
     57                     |val| Ok(Rational(val)),
     58                 )
     59             }
     60         }
     61         deserializer.deserialize_str(RationalVisitor { _x: PhantomData })
     62     }
     63 }
     64 #[cfg(test)]
     65 mod tests {
     66     use super::*;
     67     #[test]
     68     fn test_serde() -> Result<(), serde_json::Error> {
     69         assert_eq!(
     70             Ratio::new(2u8, 3u8),
     71             serde_json::from_str::<Rational::<u8>>(r#""2/3""#)?.0
     72         );
     73         assert_eq!(
     74             Ratio::new(67u8, 100u8),
     75             serde_json::from_str::<Rational::<u8>>(r#""0.67""#)?.0
     76         );
     77         Ok(())
     78     }
     79 }