1use crate::error::{TryFromNumberError, TryFromNumberErrorKind};
9#[cfg(all(
10 aws_sdk_unstable,
11 any(feature = "serde-serialize", feature = "serde-deserialize")
12))]
13use serde;
14
15#[derive(Debug, Clone, Copy, PartialEq)]
18#[cfg_attr(
19 all(aws_sdk_unstable, feature = "serde-deserialize"),
20 derive(serde::Deserialize)
21)]
22#[cfg_attr(
23 all(aws_sdk_unstable, feature = "serde-serialize"),
24 derive(serde::Serialize)
25)]
26#[cfg_attr(
27 any(
28 all(aws_sdk_unstable, feature = "serde-deserialize"),
29 all(aws_sdk_unstable, feature = "serde-serialize")
30 ),
31 serde(untagged)
32)]
33pub enum Number {
34 PosInt(u64),
36 NegInt(i64),
38 Float(f64),
40}
41
42impl Number {
45 pub fn to_f64_lossy(self) -> f64 {
48 match self {
49 Number::PosInt(v) => v as f64,
50 Number::NegInt(v) => v as f64,
51 Number::Float(v) => v,
52 }
53 }
54
55 pub fn to_f32_lossy(self) -> f32 {
58 match self {
59 Number::PosInt(v) => v as f32,
60 Number::NegInt(v) => v as f32,
61 Number::Float(v) => v as f32,
62 }
63 }
64}
65
66macro_rules! to_unsigned_integer_converter {
67 ($typ:ident, $styp:expr) => {
68 #[doc = "Converts to a `"]
69 #[doc = $styp]
70 #[doc = "`. This conversion fails if it is lossy."]
71 impl TryFrom<Number> for $typ {
72 type Error = TryFromNumberError;
73
74 fn try_from(value: Number) -> Result<Self, Self::Error> {
75 match value {
76 Number::PosInt(v) => Ok(Self::try_from(v)?),
77 Number::NegInt(v) => {
78 Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into())
79 }
80 Number::Float(v) => attempt_lossless!(v, $typ),
81 }
82 }
83 }
84 };
85
86 ($typ:ident) => {
87 to_unsigned_integer_converter!($typ, stringify!($typ));
88 };
89}
90
91macro_rules! to_signed_integer_converter {
92 ($typ:ident, $styp:expr) => {
93 #[doc = "Converts to a `"]
94 #[doc = $styp]
95 #[doc = "`. This conversion fails if it is lossy."]
96 impl TryFrom<Number> for $typ {
97 type Error = TryFromNumberError;
98
99 fn try_from(value: Number) -> Result<Self, Self::Error> {
100 match value {
101 Number::PosInt(v) => Ok(Self::try_from(v)?),
102 Number::NegInt(v) => Ok(Self::try_from(v)?),
103 Number::Float(v) => attempt_lossless!(v, $typ),
104 }
105 }
106 }
107 };
108
109 ($typ:ident) => {
110 to_signed_integer_converter!($typ, stringify!($typ));
111 };
112}
113
114macro_rules! attempt_lossless {
115 ($value: expr, $typ: ty) => {{
116 let converted = $value as $typ;
117 if (converted as f64 == $value) {
118 Ok(converted)
119 } else {
120 Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion($value).into())
121 }
122 }};
123}
124
125impl TryFrom<Number> for u64 {
127 type Error = TryFromNumberError;
128
129 fn try_from(value: Number) -> Result<Self, Self::Error> {
130 match value {
131 Number::PosInt(v) => Ok(v),
132 Number::NegInt(v) => {
133 Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into())
134 }
135 Number::Float(v) => attempt_lossless!(v, u64),
136 }
137 }
138}
139to_unsigned_integer_converter!(u32);
140to_unsigned_integer_converter!(u16);
141to_unsigned_integer_converter!(u8);
142
143impl TryFrom<Number> for i64 {
144 type Error = TryFromNumberError;
145
146 fn try_from(value: Number) -> Result<Self, Self::Error> {
147 match value {
148 Number::PosInt(v) => Ok(Self::try_from(v)?),
149 Number::NegInt(v) => Ok(v),
150 Number::Float(v) => attempt_lossless!(v, i64),
151 }
152 }
153}
154to_signed_integer_converter!(i32);
155to_signed_integer_converter!(i16);
156to_signed_integer_converter!(i8);
157
158impl TryFrom<Number> for f64 {
160 type Error = TryFromNumberError;
161
162 fn try_from(value: Number) -> Result<Self, Self::Error> {
163 match value {
164 Number::PosInt(v) => {
168 if v <= (1 << 53) {
169 Ok(v as Self)
170 } else {
171 Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into())
172 }
173 }
174 Number::NegInt(v) => {
175 if (-(1 << 53)..=(1 << 53)).contains(&v) {
176 Ok(v as Self)
177 } else {
178 Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into())
179 }
180 }
181 Number::Float(v) => Ok(v),
182 }
183 }
184}
185
186impl TryFrom<Number> for f32 {
188 type Error = TryFromNumberError;
189
190 fn try_from(value: Number) -> Result<Self, Self::Error> {
191 match value {
192 Number::PosInt(v) => {
193 if v <= (1 << 24) {
194 Ok(v as Self)
195 } else {
196 Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into())
197 }
198 }
199 Number::NegInt(v) => {
200 if (-(1 << 24)..=(1 << 24)).contains(&v) {
201 Ok(v as Self)
202 } else {
203 Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into())
204 }
205 }
206 Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()),
207 }
208 }
209}
210
211#[cfg(test)]
212mod test {
213 use super::Number;
214 use crate::error::{TryFromNumberError, TryFromNumberErrorKind};
215
216 macro_rules! to_unsigned_converter_tests {
217 ($typ:ident) => {
218 assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69);
219
220 assert!(matches!(
221 $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(),
222 TryFromNumberError {
223 kind: TryFromNumberErrorKind::OutsideIntegerRange(..)
224 }
225 ));
226
227 assert!(matches!(
228 $typ::try_from(Number::NegInt(-1i64)).unwrap_err(),
229 TryFromNumberError {
230 kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..)
231 }
232 ));
233
234 for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
235 assert!(matches!(
236 $typ::try_from(Number::Float(val)).unwrap_err(),
237 TryFromNumberError {
238 kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..)
239 }
240 ));
241 }
242 assert_eq!($typ::try_from(Number::Float(25.0)).unwrap(), 25);
243 };
244 }
245
246 #[test]
247 fn to_u64() {
248 assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64);
249
250 assert!(matches!(
251 u64::try_from(Number::NegInt(-1i64)).unwrap_err(),
252 TryFromNumberError {
253 kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..)
254 }
255 ));
256
257 for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
258 assert!(matches!(
259 u64::try_from(Number::Float(val)).unwrap_err(),
260 TryFromNumberError {
261 kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..)
262 }
263 ));
264 }
265 }
266
267 #[test]
268 fn to_u32() {
269 to_unsigned_converter_tests!(u32);
270 }
271
272 #[test]
273 fn to_u16() {
274 to_unsigned_converter_tests!(u16);
275 }
276
277 #[test]
278 fn to_u8() {
279 to_unsigned_converter_tests!(u8);
280 }
281
282 macro_rules! to_signed_converter_tests {
283 ($typ:ident) => {
284 assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69);
285 assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69);
286
287 assert!(matches!(
288 $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(),
289 TryFromNumberError {
290 kind: TryFromNumberErrorKind::OutsideIntegerRange(..)
291 }
292 ));
293
294 assert!(matches!(
295 $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(),
296 TryFromNumberError {
297 kind: TryFromNumberErrorKind::OutsideIntegerRange(..)
298 }
299 ));
300
301 for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
302 assert!(matches!(
303 u64::try_from(Number::Float(val)).unwrap_err(),
304 TryFromNumberError {
305 kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..)
306 }
307 ));
308 }
309
310 let range = || ($typ::MIN..=$typ::MAX);
311
312 for val in range().take(1024).chain(range().rev().take(1024)) {
313 assert_eq!(val, $typ::try_from(Number::Float(val as f64)).unwrap());
314 $typ::try_from(Number::Float((val as f64) + 0.1)).expect_err("not equivalent");
315 }
316 };
317 }
318
319 #[test]
320 fn to_i64() {
321 assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69);
322 assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69);
323
324 for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
325 assert!(matches!(
326 u64::try_from(Number::Float(val)).unwrap_err(),
327 TryFromNumberError {
328 kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..)
329 }
330 ));
331 }
332 let range = || (i64::MIN..=i64::MAX);
333
334 for val in range().take(1024).chain(range().rev().take(1024)) {
335 if ((val as f64) as i64) == val {
337 assert_eq!(val, i64::try_from(Number::Float(val as f64)).unwrap());
338 }
339 let fval = val as f64;
340 if (fval + 0.1).fract() != 0.0 {
342 i64::try_from(Number::Float((val as f64) + 0.1)).expect_err("not equivalent");
343 }
344 }
345 }
346
347 #[test]
348 fn to_i32() {
349 to_signed_converter_tests!(i32);
350 }
351
352 #[test]
353 fn to_i16() {
354 to_signed_converter_tests!(i16);
355 }
356
357 #[test]
358 fn to_i8() {
359 to_signed_converter_tests!(i8);
360 i8::try_from(Number::Float(-3200000.0)).expect_err("overflow");
361 i8::try_from(Number::Float(32.1)).expect_err("imprecise");
362 i8::try_from(Number::Float(i8::MAX as f64 + 0.1)).expect_err("imprecise");
363 i8::try_from(Number::Float(f64::NAN)).expect_err("nan");
364 i8::try_from(Number::Float(f64::INFINITY)).expect_err("nan");
365 }
366
367 #[test]
368 fn to_f64() {
369 assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64);
370 assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64);
371 assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64);
372 assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan());
373 assert_eq!(
374 f64::try_from(Number::Float(f64::INFINITY)).unwrap(),
375 f64::INFINITY
376 );
377 assert_eq!(
378 f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(),
379 f64::NEG_INFINITY
380 );
381
382 let significand_max_u64: u64 = 1 << 53;
383 let significand_max_i64: i64 = 1 << 53;
384
385 assert_eq!(
386 f64::try_from(Number::PosInt(significand_max_u64)).unwrap(),
387 9007199254740992f64
388 );
389
390 assert_eq!(
391 f64::try_from(Number::NegInt(significand_max_i64)).unwrap(),
392 9007199254740992f64
393 );
394 assert_eq!(
395 f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(),
396 -9007199254740992f64
397 );
398
399 assert!(matches!(
400 f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(),
401 TryFromNumberError {
402 kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..)
403 }
404 ));
405
406 assert!(matches!(
407 f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(),
408 TryFromNumberError {
409 kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..)
410 }
411 ));
412 assert!(matches!(
413 f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(),
414 TryFromNumberError {
415 kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..)
416 }
417 ));
418 }
419
420 #[test]
421 fn to_f32() {
422 assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32);
423 assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32);
424
425 let significand_max_u64: u64 = 1 << 24;
426 let significand_max_i64: i64 = 1 << 24;
427
428 assert_eq!(
429 f32::try_from(Number::PosInt(significand_max_u64)).unwrap(),
430 16777216f32
431 );
432
433 assert_eq!(
434 f32::try_from(Number::NegInt(significand_max_i64)).unwrap(),
435 16777216f32
436 );
437 assert_eq!(
438 f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(),
439 -16777216f32
440 );
441
442 assert!(matches!(
443 f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(),
444 TryFromNumberError {
445 kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..)
446 }
447 ));
448
449 assert!(matches!(
450 f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(),
451 TryFromNumberError {
452 kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..)
453 }
454 ));
455 assert!(matches!(
456 f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(),
457 TryFromNumberError {
458 kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..)
459 }
460 ));
461
462 for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
463 assert!(matches!(
464 f32::try_from(Number::Float(val)).unwrap_err(),
465 TryFromNumberError {
466 kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..)
467 }
468 ));
469 }
470 }
471
472 #[test]
473 fn to_f64_lossy() {
474 assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64);
475 assert_eq!(
476 Number::PosInt((1 << 53) + 1).to_f64_lossy(),
477 9007199254740992f64
478 );
479 assert_eq!(
480 Number::NegInt(-(1 << 53) - 1).to_f64_lossy(),
481 -9007199254740992f64
482 );
483 }
484
485 #[test]
486 fn to_f32_lossy() {
487 assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32);
488 assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32);
489 assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32);
490 assert_eq!(
491 Number::Float(1452089033.7674935).to_f32_lossy(),
492 1452089100f32
493 );
494 }
495
496 #[test]
497 #[cfg(all(
498 test,
499 aws_sdk_unstable,
500 feature = "serde-deserialize",
501 feature = "serde-serialize"
502 ))]
503 fn number_serde() {
508 let n: Number = serde_json::from_str("1.1").unwrap();
509 assert_eq!(n, Number::Float(1.1));
510 let n: Number = serde_json::from_str("1").unwrap();
511 assert_eq!(n, Number::PosInt(1));
512 let n: Number = serde_json::from_str("0").unwrap();
513 assert_eq!(n, Number::PosInt(0));
514 let n: Number = serde_json::from_str("-1").unwrap();
515 assert_eq!(n, Number::NegInt(-1));
516
517 assert_eq!("1.1", serde_json::to_string(&Number::Float(1.1)).unwrap());
518 assert_eq!("1", serde_json::to_string(&Number::PosInt(1)).unwrap());
519 assert_eq!("0", serde_json::to_string(&Number::PosInt(0)).unwrap());
520 assert_eq!("-1", serde_json::to_string(&Number::NegInt(-1)).unwrap());
521 }
522}