div_int/
serde.rs

1//! Additional functionality for serializing/deserializing `DivInt` structs with Serde.
2//!
3//! By default, `DivInt` is serialized using its semantic value, i.e. as floating-point
4//! numbers. For example, a `div_int!(35 / 20)` is serialized as `1.75`.
5//!
6//! This serialization scheme is lossy. If you want precise behavior, you can serialize `DivInt`
7//! using the numerator only by configuring Serde using the [`as_numerator`] module.
8
9use alloc::format;
10use core::fmt::{Display, Formatter};
11use serde::{Serializer, Deserializer};
12use crate::{DivInt, FromF64Approx};
13
14/// Custom Serde serializer/deserializer module that uses numerator as the serialized value.
15///
16/// The default Serialize/Deserialize implementation of [`DivInt`] serializes the struct as a
17/// floating point number. With this serializer (and Serde's `with` attribute) you can override
18/// the built-in behavior and serialize `DivInt` as a numerator instead.
19///
20/// # Examples
21/// ```
22/// use serde::{Deserialize, Serialize};
23/// use div_int::{DivInt, div_int};
24///
25/// #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
26/// struct Test {
27///   #[serde(with="div_int::serde::as_numerator")]
28///   num: DivInt<u16, 1024>,
29/// }
30///
31/// assert_eq!(serde_json::to_string(&Test{num: div_int!(100 / 1024) }).unwrap(), r#"{"num":100}"#);
32/// assert_eq!(serde_json::from_str::<Test>(r#"{"num": 123}"#).unwrap(), Test {num: div_int!(123 / 1024)});
33/// ```
34pub mod as_numerator {
35    use crate::DivInt;
36    use serde::{Deserialize, Deserializer, Serialize, Serializer};
37
38    /// Serde [serializer function] for `DivInt` that uses the numerator as the serialized value.
39    ///
40    /// [serializer function]: https://serde.rs/field-attrs.html#serialize_with
41    pub fn serialize<S: Serializer, N: Serialize, const D: u64>(di: &DivInt<N, D>, s: S) -> Result<S::Ok, S::Error> {
42        di.0.serialize(s)
43    }
44
45    /// Serde [deserializer function] for `DivInt` that expects the numerator as the deserialized.
46    ///
47    /// [deserializer function]: https://serde.rs/field-attrs.html#deserialize_with
48    pub fn deserialize<'de, De, N: Deserialize<'de>, const D: u64>(d: De) -> Result<DivInt<N, D>, De::Error>
49    where
50        De: Deserializer<'de>,
51    {
52        Ok(DivInt::from_numerator(N::deserialize(d)?))
53    }
54}
55
56
57struct OutOfRangeError(f64, &'static str, u64);
58
59impl Display for OutOfRangeError {
60    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
61        f.write_str("value ")?;
62        self.0.fmt(f)?;
63        f.write_str(" does not fit into DivInt<")?;
64        f.write_str(self.1)?;
65        f.write_str(", ")?;
66        self.2.fmt(f)?;
67        f.write_str(">")
68    }
69}
70
71#[cfg(feature="serde")]
72impl<N: Copy + Into<f64>, const D: u64> ::serde::Serialize for DivInt<N, D> {
73    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
74    where
75        S: ::serde::Serializer
76    {
77        self.to_f64().serialize(serializer)
78    }
79}
80
81impl<'de, N: FromF64Approx, const D: u64> ::serde::Deserialize<'de> for DivInt<N, D> {
82    fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>
83    where
84        De: Deserializer<'de>
85    {
86        let f = f64::deserialize(deserializer)?;
87        match DivInt::<N, D>::from_f64_approx(f) {
88            Some(v) => Ok(v),
89            None => Err(serde::de::Error::custom(OutOfRangeError(f, core::any::type_name::<N>(), D)))
90        }
91    }
92}
93
94#[cfg(test)]
95#[cfg(feature="serde")]
96mod tests {
97    use serde::{Deserialize, Serialize};
98    use super::*;
99
100    #[test]
101    fn serialize() {
102        #[derive(Serialize, Debug, Eq, PartialEq)]
103        struct Test {
104            num: DivInt<u8, 2>,
105        }
106
107        assert_eq!(serde_json::to_string(&Test{ num: DivInt::<u8, 2>::from_numerator(3) }).unwrap(), r#"{"num":1.5}"#);
108    }
109
110    #[test]
111    fn deserialize() {
112        #[derive(Deserialize, Debug, Eq, PartialEq)]
113        struct Test {
114            num: DivInt<u8, 2>,
115        }
116
117        assert_eq!(serde_json::from_str::<Test>(r#"{"num":1.5}"#).unwrap(), Test{num: DivInt::<u8, 2>::from_numerator(3)});
118    }
119}