conv/
impls.rs

1macro_rules! max_of {
2    ($name:ident) => { ::std::$name::MAX };
3}
4
5macro_rules! min_of {
6    ($name:ident) => { ::std::$name::MIN };
7}
8
9macro_rules! approx_blind {
10    (($($attrs:tt)*), $src:ty, $dst:ty, $scheme:ty) => {
11        as_item! {
12            $($attrs)*
13            impl ::ApproxFrom<$src, $scheme> for $dst {
14                type Err = ::errors::NoError;
15                #[inline]
16                fn approx_from(src: $src) -> Result<$dst, Self::Err> {
17                    Ok(src as $dst)
18                }
19            }
20        }
21    };
22}
23
24macro_rules! approx_z_to_dmax {
25    (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => {
26        as_item! {
27            $($attrs)*
28            impl ::ApproxFrom<$src, $scheme> for $dst {
29                type Err = ::errors::RangeError<$src>;
30                #[inline]
31                fn approx_from(src: $src) -> Result<$dst, Self::Err> {
32                    if !(0 <= src) {
33                        return Err(::errors::RangeError::NegOverflow(src));
34                    }
35                    if !(src <= max_of!($dst) as $src) {
36                        return Err(::errors::RangeError::PosOverflow(src));
37                    }
38                    Ok(src as $dst)
39                }
40            }
41        }
42    };
43}
44
45macro_rules! approx_to_dmax {
46    (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => {
47        as_item! {
48            $($attrs)*
49            impl ::ApproxFrom<$src, $scheme> for $dst {
50                type Err = ::errors::PosOverflow<$src>;
51                #[inline]
52                fn approx_from(src: $src) -> Result<$dst, Self::Err> {
53                    if !(src <= max_of!($dst) as $src) {
54                        return Err(::errors::PosOverflow(src));
55                    }
56                    Ok(src as $dst)
57                }
58            }
59        }
60    };
61}
62
63macro_rules! approx_dmin_to_dmax {
64    (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => {
65        as_item! {
66            $($attrs)*
67            impl ::ApproxFrom<$src, $scheme> for $dst {
68                type Err = ::errors::RangeError<$src>;
69                #[inline]
70                fn approx_from(src: $src) -> Result<$dst, Self::Err> {
71                    if !(min_of!($dst) as $src <= src) {
72                        return Err(::errors::RangeError::NegOverflow(src));
73                    }
74                    if !(src <= max_of!($dst) as $src) {
75                        return Err(::errors::RangeError::PosOverflow(src));
76                    }
77                    Ok(src as $dst)
78                }
79            }
80        }
81    }
82}
83
84macro_rules! approx_z_up {
85    (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => {
86        as_item! {
87            $($attrs)*
88            impl ::ApproxFrom<$src, $scheme> for $dst {
89                type Err = ::errors::NegOverflow<$src>;
90                #[inline]
91                fn approx_from(src: $src) -> Result<$dst, Self::Err> {
92                    if !(0 <= src) {
93                        return Err(::errors::NegOverflow(src));
94                    }
95                    Ok(src as $dst)
96                }
97            }
98        }
99    };
100}
101
102macro_rules! approx_dmin_to_dmax_no_nan {
103    (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => {
104        approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, $scheme, approx: |s| s }
105    };
106
107    (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty, approx: |$src_name:ident| $conv:expr) => {
108        approx_range_no_nan! {
109            ($($attrs)*), $src,
110            $dst, [min_of!($dst) as $src, max_of!($dst) as $src],
111            $scheme, approx: |$src_name| $conv
112        }
113    };
114}
115
116macro_rules! approx_range_no_nan {
117    (($($attrs:tt)*), $src:ty, $dst:ident, [$min:expr, $max:expr], $scheme:ty) => {
118        approx_range_no_nan! { ($($attrs)*), $src, $dst,  [$min, $max], $scheme, approx: |s| s }
119    };
120
121    (($($attrs:tt)*), $src:ty, $dst:ident, [$min:expr, $max:expr], $scheme:ty, approx: |$src_name:ident| $conv:expr) => {
122        as_item! {
123            $($attrs)*
124            impl ::ApproxFrom<$src, $scheme> for $dst {
125                type Err = ::errors::FloatError<$src>;
126                #[inline]
127                fn approx_from(src: $src) -> Result<$dst, Self::Err> {
128                    if src.is_nan() {
129                        return Err(::errors::FloatError::NotANumber(src));
130                    }
131                    let approx = { let $src_name = src; $conv };
132                    if !($min <= approx) {
133                        return Err(::errors::FloatError::NegOverflow(src));
134                    }
135                    if !(approx <= $max) {
136                        return Err(::errors::FloatError::PosOverflow(src));
137                    }
138                    Ok(approx as $dst)
139                }
140            }
141        }
142    };
143}
144
145macro_rules! num_conv {
146    (@ $src:ty=> $(,)*) => {};
147
148    (@ $src:ty=> #[32] $($tail:tt)*) => {
149        num_conv! { @ $src=> (#[cfg(target_pointer_width="32")]) $($tail)* }
150    };
151
152    (@ $src:ty=> #[64] $($tail:tt)*) => {
153        num_conv! { @ $src=> (#[cfg(target_pointer_width="64")]) $($tail)* }
154    };
155
156    (@ $src:ty=> e   $($tail:tt)*) => { num_conv! { @ $src=> () e   $($tail)* } };
157    (@ $src:ty=> n+  $($tail:tt)*) => { num_conv! { @ $src=> () n+  $($tail)* } };
158    (@ $src:ty=> n   $($tail:tt)*) => { num_conv! { @ $src=> () n   $($tail)* } };
159    (@ $src:ty=> w+  $($tail:tt)*) => { num_conv! { @ $src=> () w+  $($tail)* } };
160    (@ $src:ty=> w   $($tail:tt)*) => { num_conv! { @ $src=> () w   $($tail)* } };
161    (@ $src:ty=> aW  $($tail:tt)*) => { num_conv! { @ $src=> () aW  $($tail)* } };
162    (@ $src:ty=> nf  $($tail:tt)*) => { num_conv! { @ $src=> () nf  $($tail)* } };
163    (@ $src:ty=> fan $($tail:tt)*) => { num_conv! { @ $src=> () fan $($tail)* } };
164
165    // Exact conversion
166    (@ $src:ty=> ($($attrs:tt)*) e $dst:ty, $($tail:tt)*) => {
167        as_item! {
168            approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox }
169            approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping }
170
171            $($attrs)*
172            impl ::ValueFrom<$src> for $dst {
173                type Err = ::errors::NoError;
174                #[inline]
175                fn value_from(src: $src) -> Result<$dst, Self::Err> {
176                    Ok(src as $dst)
177                }
178            }
179        }
180        num_conv! { @ $src=> $($tail)* }
181    };
182
183    // Narrowing a signed type *into* an unsigned type where the destination type's maximum value is representable by the source type.
184    (@ $src:ty=> ($($attrs:tt)*) n+ $dst:ident, $($tail:tt)*) => {
185        as_item! {
186            approx_z_to_dmax! { ($($attrs)*), $src, $dst, ::DefaultApprox }
187            approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping }
188
189            $($attrs)*
190            impl ::ValueFrom<$src> for $dst {
191                type Err = ::errors::RangeError<$src>;
192                #[inline]
193                fn value_from(src: $src) -> Result<$dst, Self::Err> {
194                    if !(0 <= src) {
195                        return Err(::errors::RangeError::NegOverflow(src));
196                    }
197                    if !(src <= max_of!($dst) as $src) {
198                        return Err(::errors::RangeError::PosOverflow(src));
199                    }
200                    Ok(src as $dst)
201                }
202            }
203        }
204        num_conv! { @ $src=> $($tail)* }
205    };
206
207    // Narrowing an unsigned type *into* a type where the destination type's maximum value is representable by the source type.
208    (@ $src:ty=> ($($attrs:tt)*) n- $dst:ident, $($tail:tt)*) => {
209        as_item! {
210            approx_to_dmax! { ($($attrs)*), $src, $dst, ::DefaultApprox }
211            approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping }
212
213            $($attrs)*
214            impl ::ValueFrom<$src> for $dst {
215                type Err = ::errors::PosOverflow<$src>;
216                #[inline]
217                fn value_from(src: $src) -> Result<$dst, Self::Err> {
218                    if !(src <= max_of!($dst) as $src) {
219                        return Err(::errors::PosOverflow(src));
220                    }
221                    Ok(src as $dst)
222                }
223            }
224        }
225        num_conv! { @ $src=> $($tail)* }
226    };
227
228    // Narrowing where the destination type's bounds are representable by the source type.
229    (@ $src:ty=> ($($attrs:tt)*) n $dst:ident, $($tail:tt)*) => {
230        as_item! {
231            approx_dmin_to_dmax! { ($($attrs)*), $src, $dst, ::DefaultApprox }
232            approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping }
233
234            $($attrs)*
235            impl ::ValueFrom<$src> for $dst {
236                type Err = ::errors::RangeError<$src>;
237                #[inline]
238                fn value_from(src: $src) -> Result<$dst, Self::Err> {
239                    if !(min_of!($dst) as $src <= src) {
240                        return Err(::errors::RangeError::NegOverflow(src));
241                    }
242                    if !(src <= max_of!($dst) as $src) {
243                        return Err(::errors::RangeError::PosOverflow(src));
244                    }
245                    Ok(src as $dst)
246                }
247            }
248        }
249        num_conv! { @ $src=> $($tail)* }
250    };
251
252    // Widening a signed type *into* an unsigned type.
253    (@ $src:ty=> ($($attrs:tt)*) w+ $dst:ident, $($tail:tt)*) => {
254        as_item! {
255            approx_z_up! { ($($attrs)*), $src, $dst, ::DefaultApprox }
256            approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping }
257
258            $($attrs)*
259            impl ::ValueFrom<$src> for $dst {
260                type Err = ::errors::NegOverflow<$src>;
261                #[inline]
262                fn value_from(src: $src) -> Result<$dst, Self::Err> {
263                    if !(0 <= src) {
264                        return Err(::errors::NegOverflow(src));
265                    }
266                    Ok(src as $dst)
267                }
268            }
269        }
270        num_conv! { @ $src=> $($tail)* }
271    };
272
273    // Widening.
274    (@ $src:ty=> ($($attrs:tt)*) w $dst:ident, $($tail:tt)*) => {
275        as_item! {
276            approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox }
277            approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping }
278
279            $($attrs)*
280            impl ::ValueFrom<$src> for $dst {
281                type Err = ::errors::NoError;
282                #[inline]
283                fn value_from(src: $src) -> Result<$dst, Self::Err> {
284                    Ok(src as $dst)
285                }
286            }
287        }
288        num_conv! { @ $src=> $($tail)* }
289    };
290
291    // Narrowing *into* a floating-point type where the conversion is only exact within a given range.
292    (@ $src:ty=> ($($attrs:tt)*) nf [+- $bound:expr] $dst:ident, $($tail:tt)*) => {
293        as_item! {
294            approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox }
295
296            $($attrs)*
297            impl ::ValueFrom<$src> for $dst {
298                type Err = ::errors::RangeError<$src>;
299                #[inline]
300                fn value_from(src: $src) -> Result<$dst, Self::Err> {
301                    if !(-$bound <= src) {
302                        return Err(::errors::RangeError::NegOverflow(src));
303                    }
304                    if !(src <= $bound) {
305                        return Err(::errors::RangeError::PosOverflow(src));
306                    }
307                    Ok(src as $dst)
308                }
309            }
310        }
311        num_conv! { @ $src=> $($tail)* }
312    };
313
314    (@ $src:ty=> ($($attrs:tt)*) nf [, $max:expr] $dst:ident, $($tail:tt)*) => {
315        as_item! {
316            approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox }
317
318            $($attrs)*
319            impl ::ValueFrom<$src> for $dst {
320                type Err = ::errors::PosOverflow<$src>;
321                #[inline]
322                fn value_from(src: $src) -> Result<$dst, Self::Err> {
323                    if !(src <= $max) {
324                        return Err(::errors::PosOverflow(src));
325                    }
326                    Ok(src as $dst)
327                }
328            }
329        }
330        num_conv! { @ $src=> $($tail)* }
331    };
332
333    // Approximately narrowing a floating point value *into* a type where the source value is constrained by the given range of values.
334    (@ $src:ty=> ($($attrs:tt)*) fan [$min:expr, $max:expr] $dst:ident, $($tail:tt)*) => {
335        as_item! {
336            approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max],
337                ::DefaultApprox }
338            approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max],
339                ::RoundToNearest, approx: |s| s.round() }
340            approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max],
341                ::RoundToNegInf, approx: |s| s.floor() }
342            approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max],
343                ::RoundToPosInf, approx: |s| s.ceil() }
344            approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max],
345                ::RoundToZero, approx: |s| s.trunc() }
346        }
347        num_conv! { @ $src=> $($tail)* }
348    };
349
350    (@ $src:ty=> ($($attrs:tt)*) fan $dst:ident, $($tail:tt)*) => {
351        as_item! {
352            approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::DefaultApprox }
353            approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToNearest,
354                approx: |s| s.round() }
355            approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToNegInf,
356                approx: |s| s.floor() }
357            approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToPosInf,
358                approx: |s| s.ceil() }
359            approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToZero,
360                approx: |s| s.trunc() }
361        }
362        num_conv! { @ $src=> $($tail)* }
363    };
364
365    ($src:ty=> $($tail:tt)*) => {
366        num_conv! { @ $src=> $($tail)*, }
367    };
368}
369
370mod lang_ints {
371    num_conv! { i8=>  w i16, w i32, w i64, w+u8, w+u16, w+u32, w+u64, w isize, w+usize }
372    num_conv! { i16=> n i8, w i32, w i64, n+u8, w+u16, w+u32, w+u64, w isize, w+usize }
373    num_conv! { i32=> n i8, n i16, w i64, n+u8, n+u16, w+u32, w+u64 }
374    num_conv! { i64=> n i8, n i16, n i32, n+u8, n+u16, n+u32, w+u64 }
375    num_conv! { i32=> #[32] e isize, #[64] w isize, w+usize }
376    num_conv! { i64=> #[32] n isize, #[64] e isize, #[32] n+usize, #[64] w+usize }
377
378    num_conv! { u8=> n-i8, w i16, w i32, w i64, w u16, w u32, w u64, w isize, w usize }
379    num_conv! { u16=> n-i8, n-i16, w i32, w i64, n-u8, w u32, w u64, w isize, w usize }
380    num_conv! { u32=> n-i8, n-i16, n-i32, w i64, n-u8, n-u16, w u64 }
381    num_conv! { u64=> n-i8, n-i16, n-i32, n-i64, n-u8, n-u16, n-u32 }
382    num_conv! { u32=> #[32] n-isize, #[64] w isize, #[32] e usize, #[64] w usize }
383    num_conv! { u64=> n-isize, #[32] n-usize, #[64] e usize }
384
385    num_conv! { isize=> n i8, n i16, #[32] e i32, #[32] w i64, #[64] n i32, #[64] e i64 }
386    num_conv! { isize=> n+u8, n+u16, #[32] w+u32, #[32] w+u64, #[64] n+u32, #[64] w+u64 }
387    num_conv! { isize=> w+usize }
388
389    num_conv! { usize=> n-i8, n-i16, #[32] n-i32, #[32] w i64, #[64] n-i32, #[64] n-i64 }
390    num_conv! { usize=> n-u8, n-u16, #[32] e u32, #[32] w u64, #[64] n-u32, #[64] e u64 }
391    num_conv! { usize=> n-isize }
392}
393
394mod lang_floats {
395    use {ApproxFrom, ApproxScheme};
396    use ValueFrom;
397    use errors::{NoError, RangeError};
398
399    // f32 -> f64: strictly widening
400    impl<Scheme> ApproxFrom<f32, Scheme> for f64
401    where Scheme: ApproxScheme {
402        type Err = NoError;
403        #[inline]
404        fn approx_from(src: f32) -> Result<f64, Self::Err> {
405            Ok(src as f64)
406        }
407    }
408
409    impl ValueFrom<f32> for f64 {
410        type Err = NoError;
411        #[inline]
412        fn value_from(src: f32) -> Result<f64, Self::Err> {
413            Ok(src as f64)
414        }
415    }
416
417    // f64 -> f32: narrowing, approximate
418    impl ApproxFrom<f64> for f32 {
419        type Err = RangeError<f64>;
420        #[inline]
421        fn approx_from(src: f64) -> Result<f32, Self::Err> {
422            if !src.is_finite() {
423                return Ok(src as f32);
424            }
425            if !(::std::f32::MIN as f64 <= src) {
426                return Err(RangeError::NegOverflow(src));
427            }
428            if !(src <= ::std::f32::MAX as f64) {
429                return Err(RangeError::PosOverflow(src));
430            }
431            Ok(src as f32)
432        }
433    }
434}
435
436mod lang_int_to_float {
437    num_conv! { i8=>  w f32, w f64 }
438    num_conv! { i16=> w f32, w f64 }
439    num_conv! { i32=> nf [+- 16_777_216] f32, w f64 }
440    num_conv! { i64=> nf [+- 16_777_216] f32, nf [+- 9_007_199_254_740_992] f64 }
441
442    num_conv! { u8=>  w f32, w f64 }
443    num_conv! { u16=> w f32, w f64 }
444    num_conv! { u32=> nf [, 16_777_216] f32, w f64 }
445    num_conv! { u64=> nf [, 16_777_216] f32, nf [, 9_007_199_254_740_992] f64 }
446
447    num_conv! { isize=> nf [+- 16_777_216] f32,
448        #[32] w f64, #[64] nf [+- 9_007_199_254_740_992] f64 }
449    num_conv! { usize=> nf [, 16_777_216] f32,
450        #[32] w f64, #[64] nf [, 9_007_199_254_740_992] f64 }
451}
452
453mod lang_float_to_int {
454    /*
455    We use explicit ranges on narrowing float-to-int conversions because it *turns out* that just because you can cast an integer to a float, this *does not* mean you can cast it back and get the original input.  The non-explicit-range implementation of `fan` *depends* on this, so it was kinda *totally broken* for narrowing conversions.
456
457    *Yeah.*  That's floating point for you!
458    */
459    num_conv! { f32=> fan i8, fan i16,
460        fan [-2.1474836e9, 2.1474835e9] i32,
461        fan [-9.223372e18, 9.2233715e18] i64 }
462    num_conv! { f32=> fan u8, fan u16,
463        fan [0.0, 4.294967e9] u32,
464        fan [0.0, 1.8446743e19] u64 }
465    num_conv! { f32=>
466        #[32] fan [-2.1474836e9, 2.1474835e9] isize,
467        #[32] fan [0.0, 4.294967e9] usize,
468        #[64] fan [-9.223372e18, 9.2233715e18] isize,
469        #[64] fan [0.0, 1.8446743e19] usize }
470
471    num_conv! { f64=> fan i8, fan i16, fan i32,
472        fan [-9.223372036854776e18, 9.223372036854775e18] i64 }
473    num_conv! { f64=> fan u8, fan u16, fan u32,
474        fan [0.0, 1.844674407370955e19] u64 }
475    num_conv! { f64=>
476        #[32] fan isize, #[32] fan usize,
477        #[64] fan [-9.223372036854776e18, 9.223372036854775e18] isize,
478        #[64] fan [0.0, 1.844674407370955e19] usize }
479}
480
481mod lang_char_to_int {
482    use TryFrom;
483    use ValueFrom;
484    use errors::{NoError, PosOverflow};
485
486    impl TryFrom<char> for u32 {
487        type Err = NoError;
488        #[inline]
489        fn try_from(src: char) -> Result<u32, Self::Err> {
490            Ok(src as u32)
491        }
492    }
493
494    impl TryFrom<char> for usize {
495        type Err = NoError;
496        #[inline]
497        fn try_from(src: char) -> Result<usize, Self::Err> {
498            Ok(src as usize)
499        }
500    }
501
502    impl TryFrom<char> for isize {
503        type Err = NoError;
504        #[inline]
505        fn try_from(src: char) -> Result<isize, Self::Err> {
506            Ok(src as isize)
507        }
508    }
509
510    macro_rules! conv_char_to_int {
511        ($($ts:ty),* $(,)*) => {
512            $(
513                impl TryFrom<char> for $ts {
514                    type Err = PosOverflow<char>;
515                    #[inline]
516                    fn try_from(src: char) -> Result<$ts, Self::Err> {
517                        <$ts as ValueFrom<_>>::value_from(src as u32)
518                            .map_err(|_| PosOverflow(src))
519                    }
520                }
521            )*
522        };
523    }
524
525    macro_rules! conv_char_to_int_wide {
526        ($($ts:ty),* $(,)*) => {
527            $(
528                impl TryFrom<char> for $ts {
529                    type Err = NoError;
530                    #[inline]
531                    fn try_from(src: char) -> Result<$ts, Self::Err> {
532                        <$ts as ValueFrom<_>>::value_from(src as u32)
533                    }
534                }
535            )*
536        };
537    }
538
539    conv_char_to_int! { i8, i16, i32, u8, u16 }
540    conv_char_to_int_wide! { i64, u64 }
541}
542
543mod lang_int_to_char {
544    use TryFrom;
545    use ValueFrom;
546    use errors::{NoError, Unrepresentable, UnwrapOk};
547
548    impl TryFrom<u8> for char {
549        type Err = NoError;
550        #[inline]
551        fn try_from(src: u8) -> Result<char, Self::Err> {
552            Ok(src as char)
553        }
554    }
555    impl TryFrom<u16> for char {
556        type Err = Unrepresentable<u16>;
557        #[inline]
558        fn try_from(src: u16) -> Result<char, Self::Err> {
559            TryFrom::try_from(
560                <u32 as ValueFrom<_>>::value_from(src).unwrap_ok()
561            ).map_err(|_| Unrepresentable(src))
562        }
563    }
564
565    impl TryFrom<u32> for char {
566        type Err = Unrepresentable<u32>;
567        #[inline]
568        fn try_from(src: u32) -> Result<char, Self::Err> {
569            ::std::char::from_u32(src).ok_or_else(|| Unrepresentable(src))
570        }
571    }
572
573    macro_rules! conv_int_to_char {
574        ($($ts:ty),* $(,)*) => {
575            $(
576                impl TryFrom<$ts> for char {
577                    type Err = Unrepresentable<$ts>;
578                    #[inline]
579                    fn try_from(src: $ts) -> Result<char, Self::Err> {
580                        <u32 as ValueFrom<_>>::value_from(src)
581                            .map_err(|_| Unrepresentable(src))
582                            .and_then(|usv| TryFrom::try_from(usv)
583                                .map_err(|_| Unrepresentable(src)))
584                    }
585                }
586            )*
587        };
588    }
589
590    conv_int_to_char! { i8, i16, i32, i64, isize, u64, usize }
591}