1
use std::{
2
    cmp::{Eq, Ordering},
3
    hash::{Hash, Hasher},
4
};
5

            
6
use 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)]
36
pub 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

            
56
mod private {
57
    #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
58
    pub enum Never {}
59
}
60

            
61
impl Serialize for Number {
62
672
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63
672
        match self {
64
56
            Self::I8(v) => serializer.serialize_i8(*v),
65
40
            Self::I16(v) => serializer.serialize_i16(*v),
66
40
            Self::I32(v) => serializer.serialize_i32(*v),
67
40
            Self::I64(v) => serializer.serialize_i64(*v),
68
            #[cfg(feature = "integer128")]
69
20
            Self::I128(v) => serializer.serialize_i128(*v),
70
88
            Self::U8(v) => serializer.serialize_u8(*v),
71
40
            Self::U16(v) => serializer.serialize_u16(*v),
72
40
            Self::U32(v) => serializer.serialize_u32(*v),
73
40
            Self::U64(v) => serializer.serialize_u64(*v),
74
            #[cfg(feature = "integer128")]
75
20
            Self::U128(v) => serializer.serialize_u128(*v),
76
168
            Self::F32(v) => serializer.serialize_f32(v.get()),
77
80
            Self::F64(v) => serializer.serialize_f64(v.get()),
78
            #[cfg(not(doc))]
79
            Self::__NonExhaustive(never) => match *never {},
80
        }
81
672
    }
82
}
83

            
84
impl Number {
85
267418
    pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>(
86
267418
        &self,
87
267418
        visitor: V,
88
267418
    ) -> Result<V::Value, E> {
89
267418
        match self {
90
572
            Self::I8(v) => visitor.visit_i8(*v),
91
556
            Self::I16(v) => visitor.visit_i16(*v),
92
568
            Self::I32(v) => visitor.visit_i32(*v),
93
556
            Self::I64(v) => visitor.visit_i64(*v),
94
            #[cfg(feature = "integer128")]
95
282
            Self::I128(v) => visitor.visit_i128(*v),
96
249726
            Self::U8(v) => visitor.visit_u8(*v),
97
568
            Self::U16(v) => visitor.visit_u16(*v),
98
556
            Self::U32(v) => visitor.visit_u32(*v),
99
556
            Self::U64(v) => visitor.visit_u64(*v),
100
            #[cfg(feature = "integer128")]
101
423
            Self::U128(v) => visitor.visit_u128(*v),
102
4957
            Self::F32(v) => visitor.visit_f32(v.get()),
103
8098
            Self::F64(v) => visitor.visit_f64(v.get()),
104
            #[cfg(not(doc))]
105
            Self::__NonExhaustive(never) => match *never {},
106
        }
107
267418
    }
108
}
109

            
110
macro_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
13512
            pub fn new(v: $float) -> Self {
124
13512
                Self(v)
125
13512
            }
126

            
127
            #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")]
128
            #[must_use]
129
22667
            pub fn get(self) -> $float {
130
22667
                self.0
131
22667
            }
132
        }
133

            
134
        impl From<$float> for $ty {
135
13512
            fn from(v: $float) -> Self {
136
13512
                Self::new(v)
137
13512
            }
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
17575
            fn eq(&self, other: &Self) -> bool {
151
17575
                self.cmp(other).is_eq()
152
17575
            }
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
24
            fn hash<H: Hasher>(&self, state: &mut H) {
168
24
                self.0.to_bits().hash(state);
169
24
            }
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
8
            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
183
8
                Some(self.cmp(other))
184
8
            }
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
17869
            fn cmp(&self, other: &Self) -> Ordering {
212
17869
                self.0.total_cmp(&other.0)
213
17869
            }
214
        }
215
    };
216
}
217

            
218
float_ty! { F32(f32) }
219
float_ty! { F64(f64) }
220

            
221
impl Number {
222
    /// Construct a new number.
223
240402
    pub fn new(v: impl Into<Number>) -> Self {
224
240402
        v.into()
225
240402
    }
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
8340
    pub fn into_f64(self) -> f64 {
241
8340
        #[allow(clippy::cast_precision_loss)]
242
8340
        match self {
243
556
            Self::I8(v) => f64::from(v),
244
556
            Self::I16(v) => f64::from(v),
245
556
            Self::I32(v) => f64::from(v),
246
556
            Self::I64(v) => v as f64,
247
            #[cfg(feature = "integer128")]
248
278
            Self::I128(v) => v as f64,
249
556
            Self::U8(v) => f64::from(v),
250
556
            Self::U16(v) => f64::from(v),
251
556
            Self::U32(v) => f64::from(v),
252
556
            Self::U64(v) => v as f64,
253
            #[cfg(feature = "integer128")]
254
278
            Self::U128(v) => v as f64,
255
1668
            Self::F32(v) => f64::from(v.get()),
256
1668
            Self::F64(v) => v.get(),
257
            #[cfg(not(doc))]
258
            Self::__NonExhaustive(never) => match never {},
259
        }
260
8340
    }
261
}
262

            
263
macro_rules! number_from_impl {
264
    (Number::$variant:ident($wrap:ident($ty:ty))) => {
265
        impl From<$ty> for Number {
266
36881
            fn from(v: $ty) -> Number {
267
36881
                Number::$variant($wrap(v))
268
36881
            }
269
        }
270
    };
271
    (Number::$variant:ident($ty:ty)) => {
272
        impl From<$ty> for Number {
273
237773
            fn from(v: $ty) -> Number {
274
237773
                Number::$variant(v)
275
237773
            }
276
        }
277
    };
278
}
279

            
280
number_from_impl! { Number::I8(i8) }
281
number_from_impl! { Number::I16(i16) }
282
number_from_impl! { Number::I32(i32) }
283
number_from_impl! { Number::I64(i64) }
284
#[cfg(feature = "integer128")]
285
number_from_impl! { Number::I128(i128) }
286
number_from_impl! { Number::U8(u8) }
287
number_from_impl! { Number::U16(u16) }
288
number_from_impl! { Number::U32(u32) }
289
number_from_impl! { Number::U64(u64) }
290
#[cfg(feature = "integer128")]
291
number_from_impl! { Number::U128(u128) }
292
number_from_impl! { Number::F32(F32(f32)) }
293
number_from_impl! { Number::F64(F64(f64)) }
294

            
295
#[cfg(test)]
296
mod tests {
297
    use std::collections::hash_map::DefaultHasher;
298
    use std::hash::{Hash, Hasher};
299

            
300
    use super::*;
301

            
302
24
    fn hash<T: Hash>(v: &T) -> u64 {
303
24
        let mut state = DefaultHasher::new();
304
24
        v.hash(&mut state);
305
24
        state.finish()
306
24
    }
307

            
308
    #[test]
309
4
    fn test_nan() {
310
4
        assert_eq!(F32(f32::NAN), F32(f32::NAN));
311
4
        assert_eq!(F32(-f32::NAN), F32(-f32::NAN));
312
4
        assert_ne!(F32(f32::NAN), F32(-f32::NAN));
313

            
314
4
        assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN)));
315
4
        assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN)));
316
4
        assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN)));
317
4
    }
318

            
319
    #[test]
320
4
    fn test_partial_ord() {
321
4
        assert!(F32(f32::NAN) > F32(f32::INFINITY));
322
4
        assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY));
323
4
        assert!(F32(f32::NAN) == F32(f32::NAN));
324
4
    }
325
}