ron/value/
number.rs

1use std::{
2    cmp::{Eq, Ordering},
3    hash::{Hash, Hasher},
4};
5
6use serde::{de::Visitor, Serialize, Serializer};
7
8/// A wrapper for any numeric primitive type in Rust.
9///
10/// Some varints of the `Number` enum are enabled by features:
11/// - `Number::I128` and `Number::U128` by the `integer128` feature
12///
13/// To ensure that feature unification does not break `match`ing over `Number`,
14/// the `Number` enum is non-exhaustive.
15///
16/// <details>
17/// <summary>Exhaustively matching on <code>Number</code> in tests</summary>
18///
19/// If you want to ensure that you exhaustively handle every variant, you can
20/// match on the hidden `Number::__NonExhaustive` variant.
21///
22/// <div class="warning">
23/// Matching on this variant means that your code may break when RON is
24/// upgraded or when feature unification enables further variants in the
25/// <code>Number</code> enum than your code expects.
26/// </div>
27///
28/// It is your responsibility to only *ever* match on `Number::__NonExhaustive`
29/// inside tests, e.g. by using `#[cfg(test)]` on the particular match arm, to
30/// ensure that only your tests break (e.g. in CI) when your code is not
31/// exhaustively matching on every variant, e.g. after a version upgrade or
32/// feature unification.
33/// </details>
34#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
35#[cfg_attr(doc, non_exhaustive)]
36pub enum Number {
37    I8(i8),
38    I16(i16),
39    I32(i32),
40    I64(i64),
41    #[cfg(feature = "integer128")]
42    I128(i128),
43    U8(u8),
44    U16(u16),
45    U32(u32),
46    U64(u64),
47    #[cfg(feature = "integer128")]
48    U128(u128),
49    F32(F32),
50    F64(F64),
51    #[cfg(not(doc))]
52    #[allow(private_interfaces)]
53    __NonExhaustive(private::Never),
54}
55
56mod private {
57    #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
58    pub enum Never {}
59}
60
61impl Serialize for Number {
62    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63        match self {
64            Self::I8(v) => serializer.serialize_i8(*v),
65            Self::I16(v) => serializer.serialize_i16(*v),
66            Self::I32(v) => serializer.serialize_i32(*v),
67            Self::I64(v) => serializer.serialize_i64(*v),
68            #[cfg(feature = "integer128")]
69            Self::I128(v) => serializer.serialize_i128(*v),
70            Self::U8(v) => serializer.serialize_u8(*v),
71            Self::U16(v) => serializer.serialize_u16(*v),
72            Self::U32(v) => serializer.serialize_u32(*v),
73            Self::U64(v) => serializer.serialize_u64(*v),
74            #[cfg(feature = "integer128")]
75            Self::U128(v) => serializer.serialize_u128(*v),
76            Self::F32(v) => serializer.serialize_f32(v.get()),
77            Self::F64(v) => serializer.serialize_f64(v.get()),
78            #[cfg(not(doc))]
79            Self::__NonExhaustive(never) => match *never {},
80        }
81    }
82}
83
84impl Number {
85    pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>(
86        &self,
87        visitor: V,
88    ) -> Result<V::Value, E> {
89        match self {
90            Self::I8(v) => visitor.visit_i8(*v),
91            Self::I16(v) => visitor.visit_i16(*v),
92            Self::I32(v) => visitor.visit_i32(*v),
93            Self::I64(v) => visitor.visit_i64(*v),
94            #[cfg(feature = "integer128")]
95            Self::I128(v) => visitor.visit_i128(*v),
96            Self::U8(v) => visitor.visit_u8(*v),
97            Self::U16(v) => visitor.visit_u16(*v),
98            Self::U32(v) => visitor.visit_u32(*v),
99            Self::U64(v) => visitor.visit_u64(*v),
100            #[cfg(feature = "integer128")]
101            Self::U128(v) => visitor.visit_u128(*v),
102            Self::F32(v) => visitor.visit_f32(v.get()),
103            Self::F64(v) => visitor.visit_f64(v.get()),
104            #[cfg(not(doc))]
105            Self::__NonExhaustive(never) => match *never {},
106        }
107    }
108}
109
110macro_rules! float_ty {
111    ($ty:ident($float:ty)) => {
112        #[doc = concat!(
113                    "A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ",
114                    "[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ",
115                    "for a total order comparison",
116                )]
117        #[derive(Copy, Clone, Debug)] // GRCOV_EXCL_LINE
118        pub struct $ty(pub $float);
119
120        impl $ty {
121            #[doc = concat!("Construct a new [`", stringify!($ty), "`].")]
122            #[must_use]
123            pub fn new(v: $float) -> Self {
124                Self(v)
125            }
126
127            #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")]
128            #[must_use]
129            pub fn get(self) -> $float {
130                self.0
131            }
132        }
133
134        impl From<$float> for $ty {
135            fn from(v: $float) -> Self {
136                Self::new(v)
137            }
138        }
139
140        /// Partial equality comparison
141        ///
142        #[doc = concat!(
143                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
144                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
145                    "order comparison.",
146                )]
147        ///
148        /// See the [`Ord`] implementation.
149        impl PartialEq for $ty {
150            fn eq(&self, other: &Self) -> bool {
151                self.cmp(other).is_eq()
152            }
153        }
154
155        /// Equality comparison
156        ///
157        #[doc = concat!(
158                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
159                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
160                    "order comparison.",
161                )]
162        ///
163        /// See the [`Ord`] implementation.
164        impl Eq for $ty {}
165
166        impl Hash for $ty {
167            fn hash<H: Hasher>(&self, state: &mut H) {
168                self.0.to_bits().hash(state);
169            }
170        }
171
172        /// Partial ordering comparison
173        ///
174        #[doc = concat!(
175                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
176                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
177                    "order comparison.",
178                )]
179        ///
180        /// See the [`Ord`] implementation.
181        impl PartialOrd for $ty {
182            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
183                Some(self.cmp(other))
184            }
185        }
186
187        /// Ordering comparison
188        ///
189        #[doc = concat!(
190                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
191                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
192                    "order comparison.",
193                )]
194        ///
195        /// ```
196        #[doc = concat!("use ron::value::", stringify!($ty), ";")]
197        #[doc = concat!(
198                    "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ",
199                    stringify!($ty), "::new(", stringify!($float), "::INFINITY));",
200                )]
201        #[doc = concat!(
202                    "assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ",
203                    stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));",
204                )]
205        #[doc = concat!(
206                    "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ",
207                    stringify!($ty), "::new(", stringify!($float), "::NAN));",
208                )]
209        /// ```
210        impl Ord for $ty {
211            fn cmp(&self, other: &Self) -> Ordering {
212                self.0.total_cmp(&other.0)
213            }
214        }
215    };
216}
217
218float_ty! { F32(f32) }
219float_ty! { F64(f64) }
220
221impl Number {
222    /// Construct a new number.
223    pub fn new(v: impl Into<Number>) -> Self {
224        v.into()
225    }
226
227    /// Returns the [`f64`] representation of the [`Number`] regardless of
228    /// whether the number is stored as a float or integer.
229    ///
230    /// # Example
231    ///
232    /// ```
233    /// # use ron::value::Number;
234    /// let i = Number::new(5);
235    /// let f = Number::new(2.0);
236    /// assert_eq!(i.into_f64(), 5.0);
237    /// assert_eq!(f.into_f64(), 2.0);
238    /// ```
239    #[must_use]
240    pub fn into_f64(self) -> f64 {
241        #[allow(clippy::cast_precision_loss)]
242        match self {
243            Number::I8(v) => f64::from(v),
244            Number::I16(v) => f64::from(v),
245            Number::I32(v) => f64::from(v),
246            Number::I64(v) => v as f64,
247            #[cfg(feature = "integer128")]
248            Number::I128(v) => v as f64,
249            Number::U8(v) => f64::from(v),
250            Number::U16(v) => f64::from(v),
251            Number::U32(v) => f64::from(v),
252            Number::U64(v) => v as f64,
253            #[cfg(feature = "integer128")]
254            Number::U128(v) => v as f64,
255            Number::F32(v) => f64::from(v.get()),
256            Number::F64(v) => v.get(),
257            #[cfg(not(doc))]
258            Self::__NonExhaustive(never) => match never {},
259        }
260    }
261}
262
263macro_rules! number_from_impl {
264    (Number::$variant:ident($wrap:ident($ty:ty))) => {
265        impl From<$ty> for Number {
266            fn from(v: $ty) -> Number {
267                Number::$variant($wrap(v))
268            }
269        }
270    };
271    (Number::$variant:ident($ty:ty)) => {
272        impl From<$ty> for Number {
273            fn from(v: $ty) -> Number {
274                Number::$variant(v)
275            }
276        }
277    };
278}
279
280number_from_impl! { Number::I8(i8) }
281number_from_impl! { Number::I16(i16) }
282number_from_impl! { Number::I32(i32) }
283number_from_impl! { Number::I64(i64) }
284#[cfg(feature = "integer128")]
285number_from_impl! { Number::I128(i128) }
286number_from_impl! { Number::U8(u8) }
287number_from_impl! { Number::U16(u16) }
288number_from_impl! { Number::U32(u32) }
289number_from_impl! { Number::U64(u64) }
290#[cfg(feature = "integer128")]
291number_from_impl! { Number::U128(u128) }
292number_from_impl! { Number::F32(F32(f32)) }
293number_from_impl! { Number::F64(F64(f64)) }
294
295#[cfg(test)]
296mod tests {
297    use std::collections::hash_map::DefaultHasher;
298    use std::hash::{Hash, Hasher};
299
300    use super::*;
301
302    fn hash<T: Hash>(v: &T) -> u64 {
303        let mut state = DefaultHasher::new();
304        v.hash(&mut state);
305        state.finish()
306    }
307
308    #[test]
309    fn test_nan() {
310        assert_eq!(F32(f32::NAN), F32(f32::NAN));
311        assert_eq!(F32(-f32::NAN), F32(-f32::NAN));
312        assert_ne!(F32(f32::NAN), F32(-f32::NAN));
313
314        assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN)));
315        assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN)));
316        assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN)));
317    }
318
319    #[test]
320    fn test_partial_ord() {
321        assert!(F32(f32::NAN) > F32(f32::INFINITY));
322        assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY));
323        assert!(F32(f32::NAN) == F32(f32::NAN));
324    }
325}