1use std::{i8, i16, i32, i64, u8, u16, u32, u64, usize, f32, f64};
4use std::error;
5use std::fmt;
6use webcore::try_from::TryFrom;
7
8const MAX_SAFE_INTEGER_F64: i64 = 9007199254740991;
10const MIN_SAFE_INTEGER_F64: i64 = -9007199254740991;
11
12#[derive(Copy, Clone, PartialEq, Debug)]
13pub enum Storage {
14 I32( i32 ),
21 F64( f64 )
22}
23
24#[derive(Copy, Clone, PartialEq, Debug)]
26pub struct Number( Storage );
27
28#[derive(Clone, PartialEq, Eq, Debug)]
29pub enum ConversionError {
30 OutOfRange,
31 NotAnInteger
32}
33
34impl fmt::Display for ConversionError {
35 fn fmt( &self, formatter: &mut fmt::Formatter ) -> Result< (), fmt::Error > {
36 let message = error::Error::description( self );
37 write!( formatter, "{}", message )
38 }
39}
40
41impl error::Error for ConversionError {
42 fn description( &self ) -> &str {
43 match *self {
44 ConversionError::OutOfRange => "number out of range",
45 ConversionError::NotAnInteger => "number not an integer"
46 }
47 }
48}
49
50#[inline]
52pub fn get_storage( number: &Number ) -> &Storage {
53 &number.0
54}
55
56impl AsRef< Number > for Number {
57 #[inline]
58 fn as_ref( &self ) -> &Self {
59 self
60 }
61}
62
63impl From< i8 > for Number {
64 #[inline]
65 fn from( value: i8 ) -> Self {
66 Number( Storage::I32( value as i32 ) )
67 }
68}
69
70impl From< i16 > for Number {
71 #[inline]
72 fn from( value: i16 ) -> Self {
73 Number( Storage::I32( value as i32 ) )
74 }
75}
76
77impl From< i32 > for Number {
78 #[inline]
79 fn from( value: i32 ) -> Self {
80 Number( Storage::I32( value ) )
81 }
82}
83
84impl TryFrom< i64 > for Number {
85 type Error = ConversionError;
86
87 fn try_from( value: i64 ) -> Result< Self, Self::Error > {
88 if value >= i32::MIN as i64 && value <= i32::MAX as i64 {
89 Ok( Number( Storage::I32( value as i32 ) ) )
90 } else if value >= MIN_SAFE_INTEGER_F64 && value <= MAX_SAFE_INTEGER_F64 {
91 Ok( Number( Storage::F64( value as f64 ) ) )
92 } else {
93 Err( ConversionError::OutOfRange )
94 }
95 }
96}
97
98impl From< u8 > for Number {
99 #[inline]
100 fn from( value: u8 ) -> Self {
101 Number( Storage::I32( value as i32 ) )
102 }
103}
104
105impl From< u16 > for Number {
106 #[inline]
107 fn from( value: u16 ) -> Self {
108 Number( Storage::I32( value as i32 ) )
109 }
110}
111
112impl From< u32 > for Number {
113 #[inline]
114 fn from( value: u32 ) -> Self {
115 if value <= i32::MAX as u32 {
116 Number( Storage::I32( value as i32 ) )
117 } else {
118 Number( Storage::F64( value as f64 ) )
119 }
120 }
121}
122
123impl TryFrom< u64 > for Number {
124 type Error = ConversionError;
125
126 fn try_from( value: u64 ) -> Result< Self, Self::Error > {
127 if value <= i32::MAX as u64 {
128 Ok( Number( Storage::I32( value as i32 ) ) )
129 } else if value <= MAX_SAFE_INTEGER_F64 as u64 {
130 Ok( Number( Storage::F64( value as f64 ) ) )
131 } else {
132 Err( ConversionError::OutOfRange )
133 }
134 }
135}
136
137impl TryFrom< usize > for Number {
139 type Error = ConversionError;
140
141 fn try_from( value: usize ) -> Result< Self, Self::Error > {
142 if value <= i32::MAX as usize {
143 Ok( Number( Storage::I32( value as i32 ) ) )
144 } else if value <= MAX_SAFE_INTEGER_F64 as usize {
145 Ok( Number( Storage::F64( value as f64 ) ) )
146 } else {
147 Err( ConversionError::OutOfRange )
148 }
149 }
150}
151
152impl From< f32 > for Number {
153 #[inline]
154 fn from( value: f32 ) -> Self {
155 Number( Storage::F64( value as f64 ) )
156 }
157}
158
159impl From< f64 > for Number {
160 #[inline]
161 fn from( value: f64 ) -> Self {
162 Number( Storage::F64( value ) )
163 }
164}
165
166impl From< Number > for f64 {
167 #[inline]
168 fn from( number: Number ) -> Self {
169 match number.0 {
170 Storage::I32( value ) => value as f64,
171 Storage::F64( value ) => value
172 }
173 }
174}
175
176macro_rules! impl_trivial_try_from {
177 ($($kind:ty),+) => {
178 $(
179 impl TryFrom< $kind > for Number {
180 type Error = $crate::unstable::Void;
181
182 #[inline]
183 fn try_from( value: $kind ) -> Result< Self, Self::Error > {
184 Ok( value.into() )
185 }
186 }
187 )+
188 }
189}
190
191impl_trivial_try_from!( i8, i16, i32, u8, u16, u32, f32, f64 );
192
193macro_rules! impl_conversion_into_rust_types {
194 ($(into $($kind:tt),+: { from i32: $integer_callback:ident, from f64: $float_callback:ident }),+) => {
195 $(
196 $(
197 impl TryFrom< Number > for $kind {
198 type Error = ConversionError;
199
200 #[allow(trivial_numeric_casts)]
201 fn try_from( number: Number ) -> Result< Self, Self::Error > {
202 match number.0 {
203 Storage::I32( value ) => {
204 $integer_callback!( value, $kind )
205 },
206 Storage::F64( value ) => {
207 $float_callback!( value, $kind )
208 }
209 }
210 }
211 }
212 )+
213 )+
214 }
215}
216
217macro_rules! i32_to_small_integer {
218 ($value:expr, $kind:tt) => {
219 if $value <= $kind::MAX as i32 && $value >= $kind::MIN as i32 {
220 Ok( $value as $kind )
221 } else {
222 Err( ConversionError::OutOfRange )
223 }
224 }
225}
226
227macro_rules! direct_cast {
228 ($value:expr, $kind:tt) => {
229 Ok( $value as $kind )
230 }
231}
232
233macro_rules! i32_to_big_unsigned_integer {
234 ($value:expr, $kind:tt) => {
235 if $value >= 0 {
236 Ok( $value as $kind )
237 } else {
238 Err( ConversionError::OutOfRange )
239 }
240 }
241}
242
243macro_rules! f64_to_integer {
244 ($value:expr, $kind:tt) => {{
245 if $value.floor() != $value {
246 return Err( ConversionError::NotAnInteger );
247 }
248
249 if $value <= $kind::MAX as f64 && $value >= $kind::MIN as f64 {
250 Ok( $value as $kind )
251 } else {
252 Err( ConversionError::OutOfRange )
253 }
254 }}
255}
256
257impl_conversion_into_rust_types! {
258 into i8, i16, i32, u8, u16: {
259 from i32: i32_to_small_integer,
260 from f64: f64_to_integer
261 },
262 into i64: {
263 from i32: direct_cast,
264 from f64: f64_to_integer
265 },
266 into u32, u64, usize: {
267 from i32: i32_to_big_unsigned_integer,
268 from f64: f64_to_integer
269 },
270 into f64: {
271 from i32: direct_cast,
272 from f64: direct_cast
273 }
274}
275
276impl PartialEq< i8 > for Number {
277 #[inline]
278 fn eq( &self, right: &i8 ) -> bool {
279 match self.0 {
280 Storage::I32( left ) => left == *right as i32,
281 Storage::F64( left ) => left == *right as f64
282 }
283 }
284}
285
286impl PartialEq< i16 > for Number {
287 #[inline]
288 fn eq( &self, right: &i16 ) -> bool {
289 match self.0 {
290 Storage::I32( left ) => left == *right as i32,
291 Storage::F64( left ) => left == *right as f64
292 }
293 }
294}
295
296impl PartialEq< i32 > for Number {
297 #[inline]
298 fn eq( &self, right: &i32 ) -> bool {
299 match self.0 {
300 Storage::I32( left ) => left == *right,
301 Storage::F64( left ) => left == *right as f64
302 }
303 }
304}
305
306impl PartialEq< i64 > for Number {
307 #[inline]
308 fn eq( &self, right: &i64 ) -> bool {
309 match self.0 {
310 Storage::I32( left ) => left as i64 == *right,
311 Storage::F64( left ) => left == *right as f64
312 }
313 }
314}
315
316impl PartialEq< u8 > for Number {
317 #[inline]
318 fn eq( &self, right: &u8 ) -> bool {
319 match self.0 {
320 Storage::I32( left ) => left == *right as i32,
321 Storage::F64( left ) => left == *right as f64
322 }
323 }
324}
325
326impl PartialEq< u16 > for Number {
327 #[inline]
328 fn eq( &self, right: &u16 ) -> bool {
329 match self.0 {
330 Storage::I32( left ) => left == *right as i32,
331 Storage::F64( left ) => left == *right as f64
332 }
333 }
334}
335
336impl PartialEq< u32 > for Number {
337 #[inline]
338 fn eq( &self, right: &u32 ) -> bool {
339 match self.0 {
340 Storage::I32( left ) => left as i64 == *right as i64,
341 Storage::F64( left ) => left == *right as f64
342 }
343 }
344}
345
346impl PartialEq< u64 > for Number {
347 #[inline]
348 fn eq( &self, right: &u64 ) -> bool {
349 match self.0 {
350 Storage::I32( left ) => {
351 if *right >= i32::MAX as u64 {
352 return false;
354 }
355 left == *right as i32
356 },
357 Storage::F64( left ) => left == *right as f64
358 }
359 }
360}
361
362impl PartialEq< usize > for Number {
363 #[inline]
364 fn eq( &self, right: &usize ) -> bool {
365 match self.0 {
366 Storage::I32( left ) => {
367 if *right >= i32::MAX as usize {
368 return false;
370 }
371 left == *right as i32
372 },
373 Storage::F64( left ) => left == *right as f64
374 }
375 }
376}
377
378impl PartialEq< f32 > for Number {
379 #[inline]
380 fn eq( &self, right: &f32 ) -> bool {
381 match self.0 {
382 Storage::I32( left ) => left as f64 == *right as f64,
383 Storage::F64( left ) => left == *right as f64
384 }
385 }
386}
387
388impl PartialEq< f64 > for Number {
389 #[inline]
390 fn eq( &self, right: &f64 ) -> bool {
391 match self.0 {
392 Storage::I32( left ) => left as f64 == *right,
393 Storage::F64( left ) => left == *right
394 }
395 }
396}
397
398macro_rules! impl_symmetric_partial_eq {
399 ( $($kind:tt),+ ) => {
400 $(
401 impl PartialEq< Number > for $kind {
402 #[inline]
403 fn eq( &self, right: &Number ) -> bool {
404 right == self
405 }
406 }
407 )+
408 }
409}
410
411impl_symmetric_partial_eq! {
412 u8, u16, u32, u64,
413 usize,
414 i8, i16, i32, i64,
415 f32, f64
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421 use std::mem;
422 use std::u8;
423
424 const MAX_SAFE_INTEGER_F32: i32 = 8388607;
426 const MIN_SAFE_INTEGER_F32: i32 = -8388607;
427
428 trait ExampleValues: Sized {
429 fn example_values() -> Vec< Self >;
430 }
431
432 macro_rules! example_values {
433 (
434 $($kind:tt => [$($value:expr),+]),+
435 ) => {
436 $(
437 impl ExampleValues for $kind {
438 fn example_values() -> Vec< Self > { vec![ $($value),+ ] }
439 }
440 )+
441 }
442 }
443
444 example_values! {
445 u8 => [ 0, 1, u8::MAX - 1, u8::MAX ],
446 u16 => [ 0, 1, u16::MAX - 1, u16::MAX ],
447 u32 => [ 0, 1, u32::MAX - 1, u32::MAX ],
448 u64 => [ 0, 1, u64::MAX - 1, u64::MAX ],
449 usize => [ 0, 1, usize::MAX - 1, usize::MAX ],
450 i8 => [ i8::MIN, i8::MIN + 1, -1, 0, 1, i8::MAX - 1, i8::MAX ],
451 i16 => [ i16::MIN, i16::MIN + 1, -1, 0, 1, i16::MAX - 1, i16::MAX ],
452 i32 => [ i32::MIN, i32::MIN + 1, -1, 0, 1, i32::MAX - 1, i32::MAX ],
453 i64 => [ i64::MIN, i64::MIN + 1, -1, 0, 1, i64::MAX - 1, i64::MAX ],
454 f32 => [
455 f32::MIN, f32::MIN + 1.0, -1.0, 0.0, 1.0, f32::MAX - 1.0, f32::MAX,
456 -0.33, 0.33, -3.33, 3.33,
457 MIN_SAFE_INTEGER_F32 as f32,
458 MIN_SAFE_INTEGER_F32 as f32 - 100.0,
459 MAX_SAFE_INTEGER_F32 as f32,
460 MAX_SAFE_INTEGER_F32 as f32 + 100.0
461 ],
462 f64 => [
463 f64::MIN, f64::MIN + 1.0, -1.0, 0.0, 1.0, f64::MAX - 1.0, f64::MAX,
464 -0.33, 0.33, -3.33, 3.33,
465 MIN_SAFE_INTEGER_F32 as f64,
466 MIN_SAFE_INTEGER_F32 as f64 - 100.0,
467 MAX_SAFE_INTEGER_F32 as f64,
468 MAX_SAFE_INTEGER_F32 as f64 + 100.0,
469 MIN_SAFE_INTEGER_F64 as f64,
470 MIN_SAFE_INTEGER_F64 as f64 - 100.0,
471 MAX_SAFE_INTEGER_F64 as f64,
472 MAX_SAFE_INTEGER_F64 as f64 + 100.0
473 ]
474 }
475
476 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
477 enum Kind {
478 U,
479 I,
480 F
481 }
482
483 macro_rules! type_kind {
484 (u8) => (U);
485 (u16) => (U);
486 (u32) => (U);
487 (u64) => (U);
488 (usize) => (U);
489 (i8) => (I);
490 (i16) => (I);
491 (i32) => (I);
492 (i64) => (I);
493 (f32) => (F);
494 (f64) => (F);
495 }
496
497 macro_rules! is_convertible {
498 ($value:expr, $src_type:tt => $dst_type:tt) => {{
499 let src_size = mem::size_of::< $src_type >();
500 let dst_size = mem::size_of::< $dst_type >();
501 let src_kind = type_kind!( $src_type );
502 let dst_kind = type_kind!( $dst_type );
503
504 use self::Kind::*;
505 match (src_kind, dst_kind) {
506 (F, F) => {
507 if dst_size >= src_size {
508 true
509 } else {
510 let value = $value as f64;
511 let in_range = match dst_size {
512 4 => value <= MAX_SAFE_INTEGER_F32 as f64 && value >= MIN_SAFE_INTEGER_F32 as f64,
513 _ => unreachable!()
514 };
515
516 in_range && ($value as $dst_type) as $src_type == $value
517 }
518 },
519 (U, U) | (I, I) => {
520 if dst_size >= src_size {
521 true
522 } else {
523 $value >= ($dst_type::MIN as $src_type) &&
524 $value <= ($dst_type::MAX as $src_type)
525 }
526 },
527 (U, I) => {
528 if dst_size > src_size {
529 true
530 } else {
531 $value <= ($dst_type::MAX as $src_type)
532 }
533 },
534 (I, U) => {
535 if $value < (0 as $src_type) {
536 false
537 } else if dst_size >= src_size {
538 true
539 } else {
540 $value <= ($dst_type::MAX as $src_type)
541 }
542 },
543 (F, U) => {
544 ($value as f64).floor() == ($value as f64) &&
545 ($value as f64) >= ($dst_type::MIN as f64) &&
546 ($value as f64) <= ($dst_type::MAX as f64)
547 },
548 (F, I) => {
549 ($value as f64).floor() == ($value as f64) &&
550 ($value as f64) >= ($dst_type::MIN as f64) &&
551 ($value as f64) <= ($dst_type::MAX as f64)
552 },
553 (I, F) => {
554 let value = $value as i64;
555 match dst_size {
556 4 => value <= MAX_SAFE_INTEGER_F32 as i64 && value >= MIN_SAFE_INTEGER_F32 as i64,
557 8 => value <= MAX_SAFE_INTEGER_F64 as i64 && value >= MIN_SAFE_INTEGER_F64 as i64,
558 _ => unreachable!()
559 }
560 },
561 (U, F) => {
562 let value = $value as u64;
563 match dst_size {
564 4 => value <= MAX_SAFE_INTEGER_F32 as u64,
565 8 => value <= MAX_SAFE_INTEGER_F64 as u64,
566 _ => unreachable!()
567 }
568 }
569 }
570 }}
571 }
572
573 macro_rules! conversion_test_body {
574 ($value:expr, $src_type:tt, $dst_type:tt) => {{
575 let is_convertible_into_number = is_convertible!( $value, $src_type => f64 );
576 let number: Result< Number, _ > = $value.try_into();
577 let number = match number {
578 Ok( number ) => {
579 if !is_convertible_into_number {
580 panic!( "Type {} should NOT be convertible into Number yet it is: {:?}", stringify!( $src_type ), $value );
581 }
582 number
583 },
584 Err( _ ) => {
585 if is_convertible_into_number {
586 panic!( "Type {} should be convertible into Number yet it isn't: {:?}", stringify!( $src_type ), $value );
587 }
588 return;
589 }
590 };
591
592 let is_convertible = is_convertible!( $value, $src_type => $dst_type );
593 let output = number.try_into();
594 if is_convertible {
595 let result = output == Ok( $value as $dst_type );
596 assert!( result, "Conversion should succeed yet it didn't for {:?}", $value );
597 } else {
598 let result = output.is_err();
599 assert!( result, "Conversion should NOT succeed yet it did for {:?}", $value );
600 };
601 }}
602 }
603
604 macro_rules! conversion_test {
605 ($src_type:tt, $(($dst_type:tt, $test_name:ident)),+) => {
606 $(
607 #[allow(trivial_numeric_casts, const_err, unreachable_patterns)]
608 #[test]
609 fn $test_name() {
610 for value in $src_type::example_values() {
611 conversion_test_body!( value, $src_type, $dst_type );
612 }
613 }
614 )+
615 }
616 }
617
618 macro_rules! generate_conversion_tests {
619 ($raw_type:tt) => {
620 conversion_test! {
621 $raw_type,
622 (u8, into_u8),
623 (u16, into_u16),
624 (u32, into_u32),
625 (u64, into_u64),
626 (usize, into_usize),
627 (i8, into_i8),
628 (i16, into_i16),
629 (i32, into_i32),
630 (i64, into_i64),
631 (f64, into_f64)
633 }
634 }
635 }
636
637 macro_rules! generate_number_tests {
638 ($(($raw_type:tt, $name:ident)),+) => {
639 $(
640 mod $name {
641 use super::*;
642 use webcore::try_from::TryInto;
643
644 #[test]
645 fn round_trip() {
646 for left in $raw_type::example_values() {
647 let number: Number = left.into();
648 let right: $raw_type = number.try_into().unwrap();
649 assert!( left == right, "Failed for: {:?}", left );
650 }
651 }
652
653 #[test]
654 fn equality() {
655 for value in $raw_type::example_values() {
656 let number: Number = value.into();
657 assert!( number == value, "Failed for: {:?}", value );
658 assert!( value == number, "Failed for: {:?}", value );
659 }
660 }
661
662 generate_conversion_tests! { $raw_type }
663 }
664 )+
665 }
666 }
667
668 generate_number_tests! {
669 (u8, for_u8),
670 (u16, for_u16),
671 (u32, for_u32),
672 (i8, for_i8),
673 (i16, for_i16),
674 (i32, for_i32),
675 (f64, for_f64)
676 }
677
678 mod for_f32 {
679 use super::*;
680 use webcore::try_from::TryInto;
681 generate_conversion_tests! { f32 }
682 }
683
684 mod for_u64 {
685 use super::*;
686 use webcore::try_from::TryInto;
687 generate_conversion_tests! { u64 }
688 }
689
690 mod for_usize {
691 use super::*;
692 use webcore::try_from::TryInto;
693 generate_conversion_tests! { usize }
694 }
695
696 mod for_i64 {
697 use super::*;
698 use webcore::try_from::TryInto;
699 generate_conversion_tests! { u64 }
700 }
701
702 #[test]
703 fn test_number_into_f64() {
704 assert_eq!(f64::from(Number(Storage::F64(7.))), 7.);
705 assert_eq!(f64::from(Number(Storage::I32(7 ))), 7.);
706 assert_eq!({ let x : f64 = Number(Storage::F64(7.)).into(); x }, 7.);
707 assert_eq!({ let x : f64 = Number(Storage::I32(7 )).into(); x }, 7.);
708 }
709}