1use std::{
2 cmp::{Eq, Ordering},
3 hash::{Hash, Hasher},
4};
5
6use serde::{de::Visitor, Serialize, Serializer};
7
8#[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)] 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 #[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 impl PartialEq for $ty {
150 fn eq(&self, other: &Self) -> bool {
151 self.cmp(other).is_eq()
152 }
153 }
154
155 #[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 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 #[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 impl PartialOrd for $ty {
182 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
183 Some(self.cmp(other))
184 }
185 }
186
187 #[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 #[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 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 pub fn new(v: impl Into<Number>) -> Self {
224 v.into()
225 }
226
227 #[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}