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
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
10
pub enum Number {
11
    I8(i8),
12
    I16(i16),
13
    I32(i32),
14
    I64(i64),
15
    #[cfg(feature = "integer128")]
16
    I128(i128),
17
    U8(u8),
18
    U16(u16),
19
    U32(u32),
20
    U64(u64),
21
    #[cfg(feature = "integer128")]
22
    U128(u128),
23
    F32(F32),
24
    F64(F64),
25
}
26

            
27
impl Serialize for Number {
28
672
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
29
672
        match self {
30
56
            Self::I8(v) => serializer.serialize_i8(*v),
31
40
            Self::I16(v) => serializer.serialize_i16(*v),
32
40
            Self::I32(v) => serializer.serialize_i32(*v),
33
40
            Self::I64(v) => serializer.serialize_i64(*v),
34
            #[cfg(feature = "integer128")]
35
20
            Self::I128(v) => serializer.serialize_i128(*v),
36
88
            Self::U8(v) => serializer.serialize_u8(*v),
37
40
            Self::U16(v) => serializer.serialize_u16(*v),
38
40
            Self::U32(v) => serializer.serialize_u32(*v),
39
40
            Self::U64(v) => serializer.serialize_u64(*v),
40
            #[cfg(feature = "integer128")]
41
20
            Self::U128(v) => serializer.serialize_u128(*v),
42
168
            Self::F32(v) => serializer.serialize_f32(v.get()),
43
80
            Self::F64(v) => serializer.serialize_f64(v.get()),
44
        }
45
672
    }
46
}
47

            
48
impl Number {
49
263582
    pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>(
50
263582
        &self,
51
263582
        visitor: V,
52
263582
    ) -> Result<V::Value, E> {
53
263582
        match self {
54
564
            Self::I8(v) => visitor.visit_i8(*v),
55
548
            Self::I16(v) => visitor.visit_i16(*v),
56
560
            Self::I32(v) => visitor.visit_i32(*v),
57
548
            Self::I64(v) => visitor.visit_i64(*v),
58
            #[cfg(feature = "integer128")]
59
278
            Self::I128(v) => visitor.visit_i128(*v),
60
246142
            Self::U8(v) => visitor.visit_u8(*v),
61
560
            Self::U16(v) => visitor.visit_u16(*v),
62
548
            Self::U32(v) => visitor.visit_u32(*v),
63
548
            Self::U64(v) => visitor.visit_u64(*v),
64
            #[cfg(feature = "integer128")]
65
417
            Self::U128(v) => visitor.visit_u128(*v),
66
4887
            Self::F32(v) => visitor.visit_f32(v.get()),
67
7982
            Self::F64(v) => visitor.visit_f64(v.get()),
68
        }
69
263582
    }
70
}
71

            
72
macro_rules! float_ty {
73
    ($ty:ident($float:ty)) => {
74
        #[doc = concat!(
75
                    "A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ",
76
                    "[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ",
77
                    "for a total order comparison",
78
                )]
79
        #[derive(Copy, Clone, Debug)] // GRCOV_EXCL_LINE
80
        pub struct $ty(pub $float);
81

            
82
        impl $ty {
83
            #[doc = concat!("Construct a new [`", stringify!($ty), "`].")]
84
            #[must_use]
85
13320
            pub fn new(v: $float) -> Self {
86
13320
                Self(v)
87
13320
            }
88

            
89
            #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")]
90
            #[must_use]
91
22345
            pub fn get(self) -> $float {
92
22345
                self.0
93
22345
            }
94
        }
95

            
96
        impl From<$float> for $ty {
97
13320
            fn from(v: $float) -> Self {
98
13320
                Self::new(v)
99
13320
            }
100
        }
101

            
102
        /// Partial equality comparison
103
        ///
104
        #[doc = concat!(
105
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
106
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
107
                    "order comparison.",
108
                )]
109
        ///
110
        /// See the [`Ord`] implementation.
111
        impl PartialEq for $ty {
112
17325
            fn eq(&self, other: &Self) -> bool {
113
17325
                self.cmp(other).is_eq()
114
17325
            }
115
        }
116

            
117
        /// Equality comparison
118
        ///
119
        #[doc = concat!(
120
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
121
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
122
                    "order comparison.",
123
                )]
124
        ///
125
        /// See the [`Ord`] implementation.
126
        impl Eq for $ty {}
127

            
128
        impl Hash for $ty {
129
24
            fn hash<H: Hasher>(&self, state: &mut H) {
130
24
                self.0.to_bits().hash(state);
131
24
            }
132
        }
133

            
134
        /// Partial ordering comparison
135
        ///
136
        #[doc = concat!(
137
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
138
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
139
                    "order comparison.",
140
                )]
141
        ///
142
        /// See the [`Ord`] implementation.
143
        impl PartialOrd for $ty {
144
8
            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
145
8
                Some(self.cmp(other))
146
8
            }
147
        }
148

            
149
        /// Ordering comparison
150
        ///
151
        #[doc = concat!(
152
                    "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
153
                    "floating values use [`", stringify!($float), "::total_cmp`] for a total ",
154
                    "order comparison.",
155
                )]
156
        ///
157
        /// ```
158
        #[doc = concat!("use ron::value::", stringify!($ty), ";")]
159
        #[doc = concat!(
160
                    "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ",
161
                    stringify!($ty), "::new(", stringify!($float), "::INFINITY));",
162
                )]
163
        #[doc = concat!(
164
                    "assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ",
165
                    stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));",
166
                )]
167
        #[doc = concat!(
168
                    "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ",
169
                    stringify!($ty), "::new(", stringify!($float), "::NAN));",
170
                )]
171
        /// ```
172
        impl Ord for $ty {
173
17615
            fn cmp(&self, other: &Self) -> Ordering {
174
17615
                self.0.total_cmp(&other.0)
175
17615
            }
176
        }
177
    };
178
}
179

            
180
float_ty! { F32(f32) }
181
float_ty! { F64(f64) }
182

            
183
impl Number {
184
    /// Construct a new number.
185
236950
    pub fn new(v: impl Into<Number>) -> Self {
186
236950
        v.into()
187
236950
    }
188

            
189
    /// Returns the [`f64`] representation of the [`Number`] regardless of
190
    /// whether the number is stored as a float or integer.
191
    ///
192
    /// # Example
193
    ///
194
    /// ```
195
    /// # use ron::value::Number;
196
    /// let i = Number::new(5);
197
    /// let f = Number::new(2.0);
198
    /// assert_eq!(i.into_f64(), 5.0);
199
    /// assert_eq!(f.into_f64(), 2.0);
200
    /// ```
201
    #[must_use]
202
8220
    pub fn into_f64(self) -> f64 {
203
8220
        #[allow(clippy::cast_precision_loss)]
204
8220
        match self {
205
548
            Number::I8(v) => f64::from(v),
206
548
            Number::I16(v) => f64::from(v),
207
548
            Number::I32(v) => f64::from(v),
208
548
            Number::I64(v) => v as f64,
209
            #[cfg(feature = "integer128")]
210
274
            Number::I128(v) => v as f64,
211
548
            Number::U8(v) => f64::from(v),
212
548
            Number::U16(v) => f64::from(v),
213
548
            Number::U32(v) => f64::from(v),
214
548
            Number::U64(v) => v as f64,
215
            #[cfg(feature = "integer128")]
216
274
            Number::U128(v) => v as f64,
217
1644
            Number::F32(v) => f64::from(v.get()),
218
1644
            Number::F64(v) => v.get(),
219
        }
220
8220
    }
221
}
222

            
223
macro_rules! number_from_impl {
224
    (Number::$variant:ident($wrap:ident($ty:ty))) => {
225
        impl From<$ty> for Number {
226
36355
            fn from(v: $ty) -> Number {
227
36355
                Number::$variant($wrap(v))
228
36355
            }
229
        }
230
    };
231
    (Number::$variant:ident($ty:ty)) => {
232
        impl From<$ty> for Number {
233
234359
            fn from(v: $ty) -> Number {
234
234359
                Number::$variant(v)
235
234359
            }
236
        }
237
    };
238
}
239

            
240
number_from_impl! { Number::I8(i8) }
241
number_from_impl! { Number::I16(i16) }
242
number_from_impl! { Number::I32(i32) }
243
number_from_impl! { Number::I64(i64) }
244
#[cfg(feature = "integer128")]
245
number_from_impl! { Number::I128(i128) }
246
number_from_impl! { Number::U8(u8) }
247
number_from_impl! { Number::U16(u16) }
248
number_from_impl! { Number::U32(u32) }
249
number_from_impl! { Number::U64(u64) }
250
#[cfg(feature = "integer128")]
251
number_from_impl! { Number::U128(u128) }
252
number_from_impl! { Number::F32(F32(f32)) }
253
number_from_impl! { Number::F64(F64(f64)) }
254

            
255
#[cfg(test)]
256
mod tests {
257
    use std::collections::hash_map::DefaultHasher;
258
    use std::hash::{Hash, Hasher};
259

            
260
    use super::*;
261

            
262
24
    fn hash<T: Hash>(v: &T) -> u64 {
263
24
        let mut state = DefaultHasher::new();
264
24
        v.hash(&mut state);
265
24
        state.finish()
266
24
    }
267

            
268
    #[test]
269
4
    fn test_nan() {
270
4
        assert_eq!(F32(f32::NAN), F32(f32::NAN));
271
4
        assert_eq!(F32(-f32::NAN), F32(-f32::NAN));
272
4
        assert_ne!(F32(f32::NAN), F32(-f32::NAN));
273

            
274
4
        assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN)));
275
4
        assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN)));
276
4
        assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN)));
277
4
    }
278

            
279
    #[test]
280
4
    fn test_partial_ord() {
281
4
        assert!(F32(f32::NAN) > F32(f32::INFINITY));
282
4
        assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY));
283
4
        assert!(F32(f32::NAN) == F32(f32::NAN));
284
4
    }
285
}