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
            Self::__NonExhaustive(never) => match *never {},
79
        }
80
672
    }
81
}
82

            
83
impl Number {
84
263582
    pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>(
85
263582
        &self,
86
263582
        visitor: V,
87
263582
    ) -> Result<V::Value, E> {
88
263582
        match self {
89
564
            Self::I8(v) => visitor.visit_i8(*v),
90
548
            Self::I16(v) => visitor.visit_i16(*v),
91
560
            Self::I32(v) => visitor.visit_i32(*v),
92
548
            Self::I64(v) => visitor.visit_i64(*v),
93
            #[cfg(feature = "integer128")]
94
278
            Self::I128(v) => visitor.visit_i128(*v),
95
246142
            Self::U8(v) => visitor.visit_u8(*v),
96
560
            Self::U16(v) => visitor.visit_u16(*v),
97
548
            Self::U32(v) => visitor.visit_u32(*v),
98
548
            Self::U64(v) => visitor.visit_u64(*v),
99
            #[cfg(feature = "integer128")]
100
417
            Self::U128(v) => visitor.visit_u128(*v),
101
4887
            Self::F32(v) => visitor.visit_f32(v.get()),
102
7982
            Self::F64(v) => visitor.visit_f64(v.get()),
103
            Self::__NonExhaustive(never) => match *never {},
104
        }
105
263582
    }
106
}
107

            
108
macro_rules! float_ty {
109
    ($ty:ident($float:ty)) => {
110
        #[doc = concat!(
111
                    "A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ",
112
                    "[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ",
113
                    "for a total order comparison",
114
                )]
115
        #[derive(Copy, Clone, Debug)] // GRCOV_EXCL_LINE
116
        pub struct $ty(pub $float);
117

            
118
        impl $ty {
119
            #[doc = concat!("Construct a new [`", stringify!($ty), "`].")]
120
            #[must_use]
121
13320
            pub fn new(v: $float) -> Self {
122
13320
                Self(v)
123
13320
            }
124

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

            
132
        impl From<$float> for $ty {
133
13320
            fn from(v: $float) -> Self {
134
13320
                Self::new(v)
135
13320
            }
136
        }
137

            
138
        /// Partial equality comparison
139
        ///
140
        #[doc = concat!(
141
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
142
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
143
                    "order comparison.",
144
                )]
145
        ///
146
        /// See the [`Ord`] implementation.
147
        impl PartialEq for $ty {
148
17325
            fn eq(&self, other: &Self) -> bool {
149
17325
                self.cmp(other).is_eq()
150
17325
            }
151
        }
152

            
153
        /// Equality comparison
154
        ///
155
        #[doc = concat!(
156
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
157
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
158
                    "order comparison.",
159
                )]
160
        ///
161
        /// See the [`Ord`] implementation.
162
        impl Eq for $ty {}
163

            
164
        impl Hash for $ty {
165
24
            fn hash<H: Hasher>(&self, state: &mut H) {
166
24
                self.0.to_bits().hash(state);
167
24
            }
168
        }
169

            
170
        /// Partial ordering comparison
171
        ///
172
        #[doc = concat!(
173
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
174
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
175
                    "order comparison.",
176
                )]
177
        ///
178
        /// See the [`Ord`] implementation.
179
        impl PartialOrd for $ty {
180
8
            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
181
8
                Some(self.cmp(other))
182
8
            }
183
        }
184

            
185
        /// Ordering comparison
186
        ///
187
        #[doc = concat!(
188
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
189
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
190
                    "order comparison.",
191
                )]
192
        ///
193
        /// ```
194
        #[doc = concat!("use ron::value::", stringify!($ty), ";")]
195
        #[doc = concat!(
196
                    "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ",
197
                    stringify!($ty), "::new(", stringify!($float), "::INFINITY));",
198
                )]
199
        #[doc = concat!(
200
                    "assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ",
201
                    stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));",
202
                )]
203
        #[doc = concat!(
204
                    "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ",
205
                    stringify!($ty), "::new(", stringify!($float), "::NAN));",
206
                )]
207
        /// ```
208
        impl Ord for $ty {
209
17615
            fn cmp(&self, other: &Self) -> Ordering {
210
17615
                self.0.total_cmp(&other.0)
211
17615
            }
212
        }
213
    };
214
}
215

            
216
float_ty! { F32(f32) }
217
float_ty! { F64(f64) }
218

            
219
impl Number {
220
    /// Construct a new number.
221
236950
    pub fn new(v: impl Into<Number>) -> Self {
222
236950
        v.into()
223
236950
    }
224

            
225
    /// Returns the [`f64`] representation of the [`Number`] regardless of
226
    /// whether the number is stored as a float or integer.
227
    ///
228
    /// # Example
229
    ///
230
    /// ```
231
    /// # use ron::value::Number;
232
    /// let i = Number::new(5);
233
    /// let f = Number::new(2.0);
234
    /// assert_eq!(i.into_f64(), 5.0);
235
    /// assert_eq!(f.into_f64(), 2.0);
236
    /// ```
237
    #[must_use]
238
8220
    pub fn into_f64(self) -> f64 {
239
8220
        #[allow(clippy::cast_precision_loss)]
240
8220
        match self {
241
548
            Number::I8(v) => f64::from(v),
242
548
            Number::I16(v) => f64::from(v),
243
548
            Number::I32(v) => f64::from(v),
244
548
            Number::I64(v) => v as f64,
245
            #[cfg(feature = "integer128")]
246
274
            Number::I128(v) => v as f64,
247
548
            Number::U8(v) => f64::from(v),
248
548
            Number::U16(v) => f64::from(v),
249
548
            Number::U32(v) => f64::from(v),
250
548
            Number::U64(v) => v as f64,
251
            #[cfg(feature = "integer128")]
252
274
            Number::U128(v) => v as f64,
253
1644
            Number::F32(v) => f64::from(v.get()),
254
1644
            Number::F64(v) => v.get(),
255
            Self::__NonExhaustive(never) => match never {},
256
        }
257
8220
    }
258
}
259

            
260
macro_rules! number_from_impl {
261
    (Number::$variant:ident($wrap:ident($ty:ty))) => {
262
        impl From<$ty> for Number {
263
36355
            fn from(v: $ty) -> Number {
264
36355
                Number::$variant($wrap(v))
265
36355
            }
266
        }
267
    };
268
    (Number::$variant:ident($ty:ty)) => {
269
        impl From<$ty> for Number {
270
234359
            fn from(v: $ty) -> Number {
271
234359
                Number::$variant(v)
272
234359
            }
273
        }
274
    };
275
}
276

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

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

            
297
    use super::*;
298

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

            
305
    #[test]
306
4
    fn test_nan() {
307
4
        assert_eq!(F32(f32::NAN), F32(f32::NAN));
308
4
        assert_eq!(F32(-f32::NAN), F32(-f32::NAN));
309
4
        assert_ne!(F32(f32::NAN), F32(-f32::NAN));
310

            
311
4
        assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN)));
312
4
        assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN)));
313
4
        assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN)));
314
4
    }
315

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