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<T>. 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 }