ron/value/
number.rs

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