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)]
10pub 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
27impl Serialize for Number {
28 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
29 match self {
30 Self::I8(v) => serializer.serialize_i8(*v),
31 Self::I16(v) => serializer.serialize_i16(*v),
32 Self::I32(v) => serializer.serialize_i32(*v),
33 Self::I64(v) => serializer.serialize_i64(*v),
34 #[cfg(feature = "integer128")]
35 Self::I128(v) => serializer.serialize_i128(*v),
36 Self::U8(v) => serializer.serialize_u8(*v),
37 Self::U16(v) => serializer.serialize_u16(*v),
38 Self::U32(v) => serializer.serialize_u32(*v),
39 Self::U64(v) => serializer.serialize_u64(*v),
40 #[cfg(feature = "integer128")]
41 Self::U128(v) => serializer.serialize_u128(*v),
42 Self::F32(v) => serializer.serialize_f32(v.get()),
43 Self::F64(v) => serializer.serialize_f64(v.get()),
44 }
45 }
46}
47
48impl Number {
49 pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>(
50 &self,
51 visitor: V,
52 ) -> Result<V::Value, E> {
53 match self {
54 Self::I8(v) => visitor.visit_i8(*v),
55 Self::I16(v) => visitor.visit_i16(*v),
56 Self::I32(v) => visitor.visit_i32(*v),
57 Self::I64(v) => visitor.visit_i64(*v),
58 #[cfg(feature = "integer128")]
59 Self::I128(v) => visitor.visit_i128(*v),
60 Self::U8(v) => visitor.visit_u8(*v),
61 Self::U16(v) => visitor.visit_u16(*v),
62 Self::U32(v) => visitor.visit_u32(*v),
63 Self::U64(v) => visitor.visit_u64(*v),
64 #[cfg(feature = "integer128")]
65 Self::U128(v) => visitor.visit_u128(*v),
66 Self::F32(v) => visitor.visit_f32(v.get()),
67 Self::F64(v) => visitor.visit_f64(v.get()),
68 }
69 }
70}
71
72macro_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)] pub struct $ty(pub $float);
81
82 impl $ty {
83 #[doc = concat!("Construct a new [`", stringify!($ty), "`].")]
84 #[must_use]
85 pub fn new(v: $float) -> Self {
86 Self(v)
87 }
88
89 #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")]
90 #[must_use]
91 pub fn get(self) -> $float {
92 self.0
93 }
94 }
95
96 impl From<$float> for $ty {
97 fn from(v: $float) -> Self {
98 Self::new(v)
99 }
100 }
101
102 #[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 impl PartialEq for $ty {
112 fn eq(&self, other: &Self) -> bool {
113 self.cmp(other).is_eq()
114 }
115 }
116
117 #[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 impl Eq for $ty {}
127
128 impl Hash for $ty {
129 fn hash<H: Hasher>(&self, state: &mut H) {
130 self.0.to_bits().hash(state);
131 }
132 }
133
134 #[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 impl PartialOrd for $ty {
144 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
145 Some(self.cmp(other))
146 }
147 }
148
149 #[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 #[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 impl Ord for $ty {
173 fn cmp(&self, other: &Self) -> Ordering {
174 self.0.total_cmp(&other.0)
175 }
176 }
177 };
178}
179
180float_ty! { F32(f32) }
181float_ty! { F64(f64) }
182
183impl Number {
184 pub fn new(v: impl Into<Number>) -> Self {
186 v.into()
187 }
188
189 #[must_use]
202 pub fn into_f64(self) -> f64 {
203 #[allow(clippy::cast_precision_loss)]
204 match self {
205 Number::I8(v) => f64::from(v),
206 Number::I16(v) => f64::from(v),
207 Number::I32(v) => f64::from(v),
208 Number::I64(v) => v as f64,
209 #[cfg(feature = "integer128")]
210 Number::I128(v) => v as f64,
211 Number::U8(v) => f64::from(v),
212 Number::U16(v) => f64::from(v),
213 Number::U32(v) => f64::from(v),
214 Number::U64(v) => v as f64,
215 #[cfg(feature = "integer128")]
216 Number::U128(v) => v as f64,
217 Number::F32(v) => f64::from(v.get()),
218 Number::F64(v) => v.get(),
219 }
220 }
221}
222
223macro_rules! number_from_impl {
224 (Number::$variant:ident($wrap:ident($ty:ty))) => {
225 impl From<$ty> for Number {
226 fn from(v: $ty) -> Number {
227 Number::$variant($wrap(v))
228 }
229 }
230 };
231 (Number::$variant:ident($ty:ty)) => {
232 impl From<$ty> for Number {
233 fn from(v: $ty) -> Number {
234 Number::$variant(v)
235 }
236 }
237 };
238}
239
240number_from_impl! { Number::I8(i8) }
241number_from_impl! { Number::I16(i16) }
242number_from_impl! { Number::I32(i32) }
243number_from_impl! { Number::I64(i64) }
244#[cfg(feature = "integer128")]
245number_from_impl! { Number::I128(i128) }
246number_from_impl! { Number::U8(u8) }
247number_from_impl! { Number::U16(u16) }
248number_from_impl! { Number::U32(u32) }
249number_from_impl! { Number::U64(u64) }
250#[cfg(feature = "integer128")]
251number_from_impl! { Number::U128(u128) }
252number_from_impl! { Number::F32(F32(f32)) }
253number_from_impl! { Number::F64(F64(f64)) }
254
255#[cfg(test)]
256mod tests {
257 use std::collections::hash_map::DefaultHasher;
258 use std::hash::{Hash, Hasher};
259
260 use super::*;
261
262 fn hash<T: Hash>(v: &T) -> u64 {
263 let mut state = DefaultHasher::new();
264 v.hash(&mut state);
265 state.finish()
266 }
267
268 #[test]
269 fn test_nan() {
270 assert_eq!(F32(f32::NAN), F32(f32::NAN));
271 assert_eq!(F32(-f32::NAN), F32(-f32::NAN));
272 assert_ne!(F32(f32::NAN), F32(-f32::NAN));
273
274 assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN)));
275 assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN)));
276 assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN)));
277 }
278
279 #[test]
280 fn test_partial_ord() {
281 assert!(F32(f32::NAN) > F32(f32::INFINITY));
282 assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY));
283 assert!(F32(f32::NAN) == F32(f32::NAN));
284 }
285}