├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── scripts ├── safe_float_to_int.rs ├── test-matrix.py └── update-docs.py ├── src ├── errors.rs ├── impls.rs ├── lib.rs ├── macros.rs └── misc.rs └── tests ├── conv_utils.rs ├── derive_try_from.rs ├── lang_char.rs ├── lang_floats.rs ├── lang_ints.rs ├── unwraps.rs ├── use_in_generics.rs └── util └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | script: > 3 | cargo build $CARGO_FLAGS --features "$CARGO_FEATURES" 4 | && cargo test $CARGO_FLAGS --features "$CARGO_FEATURES" 5 | rust: 6 | - 1.2.0 7 | - 1.3.0 8 | - 1.4.0 9 | - 1.5.0 10 | - 1.6.0 11 | - 1.7.0 12 | - 1.8.0 13 | - 1.9.0 14 | - 1.10.0 15 | - stable 16 | - beta 17 | - nightly 18 | env: 19 | - > 20 | CARGO_FLAGS="--verbose --no-default-features" 21 | CARGO_FEATURES=std 22 | matrix: 23 | allow_failures: 24 | - rust: nightly 25 | include: 26 | - rust: nightly 27 | env: CARGO_FEATURES= 28 | branches: 29 | except: 30 | - /^issue-.*$/ 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conv" 3 | version = "0.3.4" 4 | authors = ["Daniel Keep "] 5 | 6 | description = "This crate provides a number of conversion traits with more specific semantics than those provided by 'as' or 'From'/'Into'." 7 | repository = "https://github.com/DanielKeep/rust-conv" 8 | documentation = "https://danielkeep.github.io/rust-conv/doc/conv/index.html" 9 | readme = "README.md" 10 | license = "MIT" 11 | keywords = ["from", "into", "conversion", "approximation"] 12 | 13 | exclude = [ 14 | "scripts/*", 15 | "update-docs.py", 16 | ] 17 | 18 | [features] 19 | default = ["std"] 20 | std = ["custom_derive/std"] 21 | 22 | [dependencies] 23 | custom_derive = { version = "0.1.5", default-features = false } 24 | 25 | [dev-dependencies] 26 | quickcheck = "0.2.21, < 0.2.25" 27 | 28 | winapi = "< 0.2.6" # 0.2.6 is incompatible with Rust < 1.4 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Daniel Keep 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # `conv` 3 | 4 | This crate provides a number of conversion traits with more specific semantics than those provided by `as` or `From`/`Into`. 5 | 6 | The goal with the traits provided here is to be more specific about what generic code can rely on, as well as provide reasonably self-describing alternatives to the standard `From`/`Into` traits. For example, the although `T: From` might be satisfied in generic code, this says nothing about what *kind* of conversion that represents. 7 | 8 | In addition, `From`/`Into` provide no facility for a conversion failing, meaning that implementations may need to choose between conversions that may not be valid, or panicking; neither option is appealing in general. 9 | 10 | **Links** 11 | 12 | * [Latest Release](https://crates.io/crates/conv/) 13 | * [Latest Docs](https://danielkeep.github.io/rust-conv/doc/conv/index.html) 14 | * [Repository](https://github.com/DanielKeep/rust-conv) 15 | 16 | ## Compatibility 17 | 18 | `conv` is compatible with Rust 1.2 and higher. 19 | 20 | ## Examples 21 | 22 | ```rust 23 | # extern crate conv; 24 | # use conv::*; 25 | # fn main() { 26 | // This *cannot* fail, so we can use `unwrap_ok` to discard the `Result`. 27 | assert_eq!(u8::value_from(0u8).unwrap_ok(), 0u8); 28 | 29 | // This *can* fail. Specifically, it can overflow toward negative infinity. 30 | assert_eq!(u8::value_from(0i8), Ok(0u8)); 31 | assert_eq!(u8::value_from(-1i8), Err(NegOverflow(-1))); 32 | 33 | // This can overflow in *either* direction; hence the change to `RangeError`. 34 | assert_eq!(u8::value_from(-1i16), Err(RangeError::NegOverflow(-1))); 35 | assert_eq!(u8::value_from(0i16), Ok(0u8)); 36 | assert_eq!(u8::value_from(256i16), Err(RangeError::PosOverflow(256))); 37 | 38 | // We can use the extension traits to simplify this a little. 39 | assert_eq!(u8::value_from(-1i16).unwrap_or_saturate(), 0u8); 40 | assert_eq!(u8::value_from(0i16).unwrap_or_saturate(), 0u8); 41 | assert_eq!(u8::value_from(256i16).unwrap_or_saturate(), 255u8); 42 | 43 | // Obviously, all integers can be "approximated" using the default scheme (it 44 | // doesn't *do* anything), but they can *also* be approximated with the 45 | // `Wrapping` scheme. 46 | assert_eq!( 47 | >::approx_from(400u16), 48 | Err(PosOverflow(400))); 49 | assert_eq!( 50 | >::approx_from(400u16), 51 | Ok(144u8)); 52 | 53 | // This is rather inconvenient; as such, there are a number of convenience 54 | // extension methods available via `ConvUtil` and `ConvAsUtil`. 55 | assert_eq!(400u16.approx(), Err::(PosOverflow(400))); 56 | assert_eq!(400u16.approx_by::(), Ok::(144u8)); 57 | assert_eq!(400u16.approx_as::(), Err(PosOverflow(400))); 58 | assert_eq!(400u16.approx_as_by::(), Ok(144)); 59 | 60 | // Integer -> float conversions *can* fail due to limited precision. 61 | // Once the continuous range of exactly representable integers is exceeded, the 62 | // provided implementations fail with overflow errors. 63 | assert_eq!(f32::value_from(16_777_216i32), Ok(16_777_216.0f32)); 64 | assert_eq!(f32::value_from(16_777_217i32), Err(RangeError::PosOverflow(16_777_217))); 65 | 66 | // Float -> integer conversions have to be done using approximations. Although 67 | // exact conversions are *possible*, "advertising" this with an implementation 68 | // is misleading. 69 | // 70 | // Note that `DefaultApprox` for float -> integer uses whatever rounding 71 | // mode is currently active (*i.e.* whatever `as` would do). 72 | assert_eq!(41.0f32.approx(), Ok(41u8)); 73 | assert_eq!(41.3f32.approx(), Ok(41u8)); 74 | assert_eq!(41.5f32.approx(), Ok(41u8)); 75 | assert_eq!(41.8f32.approx(), Ok(41u8)); 76 | assert_eq!(42.0f32.approx(), Ok(42u8)); 77 | 78 | assert_eq!(255.0f32.approx(), Ok(255u8)); 79 | assert_eq!(256.0f32.approx(), Err::(FloatError::PosOverflow(256.0))); 80 | 81 | // Sometimes, it can be useful to saturate the conversion from float to 82 | // integer directly, then account for NaN as input separately. The `Saturate` 83 | // extension trait exists for this reason. 84 | assert_eq!((-23.0f32).approx_as::().saturate(), Ok(0)); 85 | assert_eq!(302.0f32.approx_as::().saturate(), Ok(255u8)); 86 | assert!(std::f32::NAN.approx_as::().saturate().is_err()); 87 | 88 | // If you really don't care about the specific kind of error, you can just rely 89 | // on automatic conversion to `GeneralErrorKind`. 90 | fn too_many_errors() -> Result<(), GeneralErrorKind> { 91 | assert_eq!({let r: u8 = try!(0u8.value_into()); r}, 0u8); 92 | assert_eq!({let r: u8 = try!(0i8.value_into()); r}, 0u8); 93 | assert_eq!({let r: u8 = try!(0i16.value_into()); r}, 0u8); 94 | assert_eq!({let r: u8 = try!(0.0f32.approx()); r}, 0u8); 95 | Ok(()) 96 | } 97 | # let _ = too_many_errors(); 98 | # } 99 | ``` 100 | 101 | ## Change Log 102 | 103 | ### v0.3.2 104 | 105 | - Added integer ↔ `char` conversions. 106 | - Added missing `isize`/`usize` → `f32`/`f64` conversions. 107 | - Fixed the error type of `i64` → `usize` for 64-bit targets. 108 | 109 | ### v0.3.1 110 | 111 | - Change to `unwrap_ok` for better codegen (thanks bluss). 112 | - Fix for Rust breaking change (code in question was dodgy anyway; thanks m4rw3r). 113 | 114 | ### v0.3.0 115 | 116 | - Added an `Error` constraint to all `Err` associated types. This will break any user-defined conversions where the `Err` type does not implement `Error`. 117 | - Renamed the `Overflow` and `Underflow` errors to `PosOverflow` and `NegOverflow` respectively. In the context of floating point conversions, "underflow" usually means the value was too close to zero to correctly represent. 118 | 119 | ### v0.2.1 120 | 121 | - Added `ConvUtil::into_as` as a shortcut for `Into::::into`. 122 | - Added `#[inline]` attributes. 123 | - Added `Saturate::saturate`, which can saturate `Result`s arising from over/underflow. 124 | 125 | ### v0.2.0 126 | 127 | - Changed all error types to include the original input as payload. This breaks pretty much *everything*. Sorry about that. On the bright side, there's now no downside to using the conversion traits for non-`Copy` types. 128 | - Added the normal rounding modes for float → int approximations: `RoundToNearest`, `RoundToNegInf`, `RoundToPosInf`, and `RoundToZero`. 129 | - `ApproxWith` is now subsumed by a pair of extension traits (`ConvUtil` and `ConvAsUtil`), that also have shortcuts for `TryInto` and `ValueInto` so that you can specify the destination type on the method. 130 | -------------------------------------------------------------------------------- /scripts/safe_float_to_int.rs: -------------------------------------------------------------------------------- 1 | // cargo-deps: ieee754="0.2.1" 2 | /*! 3 | Experimentally derives, for each (int, float) pair, the largest and smallest integer values that survive round-tripping through the float types. 4 | 5 | This is to verify *exactly* what the safe range for float-to-int conversions is. 6 | */ 7 | extern crate ieee754; 8 | use std::fmt; 9 | use ieee754::Ieee754; 10 | 11 | macro_rules! limits { 12 | ($src:ty => $dst:ident; < $min:expr, > $max:expr) => { 13 | limits!($src => $dst; < $min); 14 | limits!($src => $dst; > $max); 15 | }; 16 | ($src:ty => $dst:ident; > $max:expr) => { 17 | { 18 | let mut cur: $src = $max; 19 | if ((cur as $dst) as $src) != cur { 20 | panic!("safe {} max: not found; initial limit too high!", stringify!($src => $dst)); 21 | } 22 | loop { 23 | let next = (cur + 1.0).max(cur.next()); 24 | let next_int = next as $dst; 25 | let next_rnd = next_int as $src; 26 | if next_rnd != next { 27 | println!("safe {} max: {}, {:e}, {:+x}", 28 | stringify!($src => $dst), cur, cur, FloatHex(cur)); 29 | break; 30 | } else { 31 | cur = next; 32 | } 33 | } 34 | } 35 | }; 36 | ($src:ty => $dst:ident; < $min:expr) => { 37 | { 38 | let mut cur: $src = $min; 39 | if ((cur as $dst) as $src) != cur { 40 | panic!("safe {} min: not found; initial limit too low!", stringify!($src => $dst)); 41 | } 42 | loop { 43 | let next = (cur - 1.0).min(cur.prev()); 44 | let next_int = next as $dst; 45 | let next_rnd = next_int as $src; 46 | if next_rnd != next { 47 | println!("\rsafe {} min: {:+}, {:+e}, {:+x}", 48 | stringify!($src => $dst), cur, cur, FloatHex(cur)); 49 | break; 50 | } else { 51 | cur = next; 52 | } 53 | } 54 | } 55 | }; 56 | } 57 | 58 | fn main() { 59 | limits!(f32 => i8; < -120.0, > 120.0); 60 | limits!(f32 => i16; < -32000.0, > 32000.0); 61 | limits!(f32 => i32; < -2147480000.0, > 2147480000.0); 62 | limits!(f32 => i64; < -9223300000000000000.0, > 9223300000000000000.0); 63 | limits!(f32 => u8; > 250.0); 64 | limits!(f32 => u16; > 64000.0); 65 | limits!(f32 => u32; > 4290000000.0); 66 | limits!(f32 => u64; > 18446700000000000000.0); 67 | 68 | limits!(f64 => i8; < -120.0, > 120.0); 69 | limits!(f64 => i16; < -32000.0, > 32000.0); 70 | limits!(f64 => i32; < -2147480000.0, > 2147480000.0); 71 | limits!(f64 => i64; < -9223372036854770000.0, > 9223372036854700000.0); 72 | limits!(f64 => u8; > 250.0); 73 | limits!(f64 => u16; > 64000.0); 74 | limits!(f64 => u32; > 4290000000.0); 75 | limits!(f64 => u64; > 18446744073709500000.0); 76 | } 77 | 78 | struct FloatHex(pub T); 79 | 80 | impl fmt::LowerHex for FloatHex { 81 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 82 | use std::num::FpCategory; 83 | 84 | fn write_sig(fmt: &mut fmt::Formatter, sig: u32) -> fmt::Result { 85 | let mut sig = sig << 9; 86 | loop { 87 | let nib = sig >> 28; 88 | try!(fmt.write_str(match nib { 89 | 0 => "0", 1 => "1", 2 => "2", 3 => "3", 90 | 4 => "4", 5 => "5", 6 => "6", 7 => "7", 91 | 8 => "8", 9 => "9", 10 => "a", 11 => "b", 92 | 12 => "c", 13 => "d", 14 => "e", _ => "f", 93 | })); 94 | sig <<= 4; 95 | if sig == 0 { break; } 96 | } 97 | Ok(()) 98 | } 99 | 100 | fn write_exp(fmt: &mut fmt::Formatter, exp: i16) -> fmt::Result { 101 | try!(write!(fmt, "p{}", exp)); 102 | Ok(()) 103 | } 104 | 105 | let v = self.0; 106 | 107 | match v.classify() { 108 | FpCategory::Nan => { 109 | try!(fmt.write_str("nan")); 110 | }, 111 | FpCategory::Infinite => { 112 | if v.is_sign_negative() { 113 | try!(fmt.write_str("-")); 114 | } else if fmt.sign_plus() { 115 | try!(fmt.write_str("+")); 116 | } 117 | try!(fmt.write_str("infinity")); 118 | }, 119 | FpCategory::Zero => { 120 | if v.is_sign_negative() { 121 | try!(fmt.write_str("-")); 122 | } else if fmt.sign_plus() { 123 | try!(fmt.write_str("+")); 124 | } 125 | try!(fmt.write_str("0x0p0")); 126 | }, 127 | FpCategory::Subnormal => { 128 | let (neg, exp, sig) = v.decompose(); 129 | if neg { try!(fmt.write_str("-")); } 130 | else if fmt.sign_plus() { try!(fmt.write_str("+")); } 131 | try!(fmt.write_str("0x0.")); 132 | try!(write_sig(fmt, sig)); 133 | try!(write_exp(fmt, exp)); 134 | }, 135 | FpCategory::Normal => { 136 | let (neg, exp, sig) = v.decompose(); 137 | if neg { try!(fmt.write_str("-")); } 138 | else if fmt.sign_plus() { try!(fmt.write_str("+")); } 139 | try!(fmt.write_str("0x1.")); 140 | try!(write_sig(fmt, sig)); 141 | try!(write_exp(fmt, exp)); 142 | }, 143 | } 144 | 145 | Ok(()) 146 | } 147 | } 148 | 149 | impl fmt::LowerHex for FloatHex { 150 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 151 | use std::num::FpCategory; 152 | 153 | fn write_sig(fmt: &mut fmt::Formatter, sig: u64) -> fmt::Result { 154 | let mut sig = sig << 13; 155 | loop { 156 | let nib = sig >> 60; 157 | try!(fmt.write_str(match nib { 158 | 0 => "0", 1 => "1", 2 => "2", 3 => "3", 159 | 4 => "4", 5 => "5", 6 => "6", 7 => "7", 160 | 8 => "8", 9 => "9", 10 => "a", 11 => "b", 161 | 12 => "c", 13 => "d", 14 => "e", _ => "f", 162 | })); 163 | sig <<= 4; 164 | if sig == 0 { break; } 165 | } 166 | Ok(()) 167 | } 168 | 169 | fn write_exp(fmt: &mut fmt::Formatter, exp: i16) -> fmt::Result { 170 | try!(write!(fmt, "p{}", exp)); 171 | Ok(()) 172 | } 173 | 174 | let v = self.0; 175 | 176 | match v.classify() { 177 | FpCategory::Nan => { 178 | try!(fmt.write_str("nan")); 179 | }, 180 | FpCategory::Infinite => { 181 | if v.is_sign_negative() { 182 | try!(fmt.write_str("-")); 183 | } else if fmt.sign_plus() { 184 | try!(fmt.write_str("+")); 185 | } 186 | try!(fmt.write_str("infinity")); 187 | }, 188 | FpCategory::Zero => { 189 | if v.is_sign_negative() { 190 | try!(fmt.write_str("-")); 191 | } else if fmt.sign_plus() { 192 | try!(fmt.write_str("+")); 193 | } 194 | try!(fmt.write_str("0x0p0")); 195 | }, 196 | FpCategory::Subnormal => { 197 | let (neg, exp, sig) = v.decompose(); 198 | if neg { try!(fmt.write_str("-")); } 199 | else if fmt.sign_plus() { try!(fmt.write_str("+")); } 200 | try!(fmt.write_str("0x0.")); 201 | try!(write_sig(fmt, sig)); 202 | try!(write_exp(fmt, exp)); 203 | }, 204 | FpCategory::Normal => { 205 | let (neg, exp, sig) = v.decompose(); 206 | if neg { try!(fmt.write_str("-")); } 207 | else if fmt.sign_plus() { try!(fmt.write_str("+")); } 208 | try!(fmt.write_str("0x1.")); 209 | try!(write_sig(fmt, sig)); 210 | try!(write_exp(fmt, exp)); 211 | }, 212 | } 213 | 214 | Ok(()) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /scripts/test-matrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | # Copyright ⓒ 2016 Daniel Keep. 5 | # 6 | # Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 8 | # ), at your option. All 9 | # files in the project carrying such notice may not be copied, modified, 10 | # or distributed except according to those terms. 11 | 12 | import os.path 13 | import re 14 | import subprocess 15 | import sys 16 | import yaml 17 | from itertools import chain 18 | 19 | LOG_DIR = os.path.join('local', 'tests') 20 | 21 | TRACE = os.environ.get('TRACE_TEST_MATRIX', '') != '' 22 | USE_ANSI = True if sys.platform != 'win32' else os.environ.get('FORCE_ANSI', '') != '' or os.environ.get('ConEmuANSI', 'OFF') == 'ON' 23 | 24 | def main(): 25 | travis = yaml.load(open('.travis.yml')) 26 | script = translate_script(travis['script']) 27 | default_rust_vers = travis['rust'] 28 | # env = {e[0].strip(): e[1].strip() for e in ( 29 | # e.split('=', maxsplit=1) for e in travis['env'])} 30 | 31 | matrix_includes = travis.get('matrix', {}).get('include', []) 32 | 33 | vers = set(default_rust_vers) 34 | include_vers = [] 35 | exclude_vers = set() 36 | 37 | if not os.path.exists(LOG_DIR): 38 | os.makedirs(LOG_DIR) 39 | 40 | for arg in sys.argv[1:]: 41 | if arg in vers and arg not in include_vers: 42 | include_vers.append(arg) 43 | elif arg.startswith('-') and arg[1:] in vers: 44 | exclude_vers.add(arg[1:]) 45 | else: 46 | msg("Don't know how to deal with argument `%s`." % arg) 47 | sys.exit(1) 48 | 49 | if include_vers == []: 50 | include_vers = default_rust_vers[:] 51 | 52 | rust_vers = [v for v in include_vers if v not in exclude_vers] 53 | msg('Tests will be run for: %s' % ', '.join(rust_vers)) 54 | 55 | results = [] 56 | for rust_ver in rust_vers: 57 | seq_id = 0 58 | for env_var_str in travis.get('env', [""]): 59 | env_vars = parse_env_vars(env_var_str) 60 | for row in chain([{}], matrix_includes): 61 | if row.get('rust', None) not in (None, rust_ver): 62 | continue 63 | 64 | row_env_vars = parse_env_vars(row.get('env', "")) 65 | 66 | cmd_env = {} 67 | cmd_env.update(env_vars) 68 | cmd_env.update(row_env_vars) 69 | 70 | success = run_script(script, rust_ver, seq_id, cmd_env) 71 | results.append((rust_ver, seq_id, success)) 72 | seq_id += 1 73 | 74 | print("") 75 | 76 | msg('Results:') 77 | for rust_ver, seq_id, success in results: 78 | msg('%s #%d: %s' % (rust_ver, seq_id, 'OK' if success else 'Failed!')) 79 | 80 | def msg(*args): 81 | if USE_ANSI: sys.stdout.write('\x1b[1;34m') 82 | sys.stdout.write('> ') 83 | if USE_ANSI: sys.stdout.write('\x1b[1;32m') 84 | for arg in args: 85 | sys.stdout.write(str(arg)) 86 | if USE_ANSI: sys.stdout.write('\x1b[0m') 87 | sys.stdout.write('\n') 88 | sys.stdout.flush() 89 | 90 | def msg_trace(*args): 91 | if TRACE: 92 | if USE_ANSI: sys.stderr.write('\x1b[1;31m') 93 | sys.stderr.write('$ ') 94 | if USE_ANSI: sys.stderr.write('\x1b[0m') 95 | for arg in args: 96 | sys.stderr.write(str(arg)) 97 | sys.stderr.write('\n') 98 | sys.stderr.flush() 99 | 100 | def parse_env_vars(s): 101 | env_vars = {} 102 | for m in re.finditer(r"""([A-Za-z0-9_]+)=(?:"([^"]+)"|(\S*))""", s.strip()): 103 | k = m.group(1) 104 | v = m.group(2) or m.group(3) 105 | env_vars[k] = v 106 | return env_vars 107 | 108 | def run_script(script, rust_ver, seq_id, env): 109 | target_dir = os.path.join('target', '%s-%d' % (rust_ver, seq_id)) 110 | log_path = os.path.join(LOG_DIR, '%s-%d.log' % (rust_ver, seq_id)) 111 | log_file = open(log_path, 'wt') 112 | msg('Running tests for %s #%d...' % (rust_ver, seq_id)) 113 | success = True 114 | 115 | def sub_env(m): 116 | name = m.group(1) or m.group(2) 117 | return cmd_env[name] 118 | 119 | log_file.write('# %s #%d\n' % (rust_ver, seq_id)) 120 | for k, v in env.items(): 121 | log_file.write('# %s=%r\n' % (k, v)) 122 | 123 | cmd_env = os.environ.copy() 124 | cmd_env['CARGO_TARGET_DIR'] = target_dir 125 | cmd_env.update(env) 126 | 127 | for cmd in script: 128 | cmd = re.sub(r"\$(?:([A-Za-z0-9_]+)|{([A-Za-z0-9_]+)})\b", sub_env, cmd) 129 | cmd_str = '> multirust run %s %s' % (rust_ver, cmd) 130 | log_file.write(cmd_str) 131 | log_file.write("\n") 132 | log_file.flush() 133 | success = sh( 134 | 'multirust run %s %s' % (rust_ver, cmd), 135 | checked=False, 136 | stdout=log_file, stderr=log_file, 137 | env=cmd_env, 138 | ) 139 | if not success: 140 | log_file.write('Command failed.\n') 141 | log_file.flush() 142 | break 143 | msg('... ', 'OK' if success else 'Failed!') 144 | log_file.close() 145 | return success 146 | 147 | def sh(cmd, env=None, stdout=None, stderr=None, checked=True): 148 | msg_trace('sh(%r, env=%r)' % (cmd, env)) 149 | try: 150 | subprocess.check_call(cmd, env=env, stdout=stdout, stderr=stderr, shell=True) 151 | except: 152 | msg_trace('FAILED!') 153 | if checked: 154 | raise 155 | else: 156 | return False 157 | if not checked: 158 | return True 159 | 160 | def translate_script(script): 161 | parts = script.split("&&") 162 | return [p.strip() for p in parts] 163 | 164 | if __name__ == '__main__': 165 | main() 166 | -------------------------------------------------------------------------------- /scripts/update-docs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | # Copyright ⓒ 2016 Daniel Keep. 5 | # 6 | # Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 8 | # ), at your option. All 9 | # files in the project carrying such notice may not be copied, modified, 10 | # or distributed except according to those terms. 11 | 12 | import distutils.dir_util 13 | import os 14 | import shutil 15 | import subprocess 16 | import sys 17 | import tempfile 18 | import time 19 | 20 | DOC_ARGS = '--no-deps' 21 | DOC_FEATURES = "" 22 | DOC_TARGET_BRANCH = 'gh-pages' 23 | TEMP_CHECKOUT_PREFIX = 'gh-pages-checkout-' 24 | TEMP_OUTPUT_PREFIX = 'gh-pages-generated-' 25 | 26 | USE_ANSI = True if sys.platform != 'win32' else os.environ.get('FORCE_ANSI', '') != '' 27 | TRACE_UPDATE_DOCS = os.environ.get('TRACE_UPDATE_DOCS', '') != '' 28 | 29 | def sh(cmd): 30 | msg_trace('sh(%r)' % cmd) 31 | try: 32 | subprocess.check_call(cmd, shell=True) 33 | except: 34 | msg_trace('FAILED!') 35 | raise 36 | 37 | def sh_eval(cmd, codec='utf-8', dont_strip=False): 38 | msg_trace('sh_eval(%r)' % cmd) 39 | result = None 40 | try: 41 | result = subprocess.check_output(cmd, shell=True).decode(codec) 42 | if not dont_strip: 43 | result = result.strip() 44 | except: 45 | msg_trace('FAILED!') 46 | raise 47 | return result 48 | 49 | def msg(*args): 50 | if USE_ANSI: sys.stdout.write('\x1b[1;34m') 51 | sys.stdout.write('> ') 52 | if USE_ANSI: sys.stdout.write('\x1b[1;32m') 53 | for arg in args: 54 | sys.stdout.write(str(arg)) 55 | if USE_ANSI: sys.stdout.write('\x1b[0m') 56 | sys.stdout.write('\n') 57 | sys.stdout.flush() 58 | 59 | def msg_trace(*args): 60 | if TRACE_UPDATE_DOCS: 61 | if USE_ANSI: sys.stderr.write('\x1b[1;31m') 62 | sys.stderr.write('$ ') 63 | if USE_ANSI: sys.stderr.write('\x1b[0m') 64 | for arg in args: 65 | sys.stderr.write(str(arg)) 66 | sys.stderr.write('\n') 67 | sys.stderr.flush() 68 | 69 | def copytree(src, dst): 70 | msg_trace('copytree(%r, %r)' % (src, dst)) 71 | distutils.dir_util.copy_tree(src=src, dst=dst) 72 | 73 | def really_rmtree(path): 74 | msg_trace('really_rmtree(%r)' % path) 75 | 76 | WAIT_TIME_SECS = 1.0 77 | MAX_TRIES = 10 78 | 79 | def on_error(func, path, exc_info): 80 | """ 81 | Error handler for ``shutil.rmtree``. 82 | 83 | If the error is due to an access error (read only file) 84 | it attempts to add write permission and then retries. 85 | 86 | If the error is for another reason it re-raises the error. 87 | 88 | Usage: ``shutil.rmtree(path, onerror=on_error)`` 89 | 90 | From _. 91 | """ 92 | import stat 93 | if not os.access(path, os.W_OK): 94 | # Is the error an access error ? 95 | os.chmod(path, stat.S_IWUSR) 96 | func(path) 97 | else: 98 | raise 99 | 100 | for _ in range(MAX_TRIES): 101 | failed = True 102 | try: 103 | msg_trace('shutil.rmtree(%r)' % path) 104 | shutil.rmtree(path, onerror=on_error) 105 | failed = False 106 | except WindowsError: 107 | time.sleep(WAIT_TIME_SECS) 108 | if not failed: return 109 | 110 | msg('Warning: failed to remove directory %r' % path) 111 | 112 | def init_doc_branch(): 113 | msg("Initialising %s branch" % DOC_TARGET_BRANCH) 114 | 115 | dir = os.getcwd() 116 | msg_trace('dir = %r' % dir) 117 | 118 | tmp = tempfile.mkdtemp(prefix=TEMP_CHECKOUT_PREFIX) 119 | msg_trace('tmp = %r' % tmp) 120 | 121 | try: 122 | msg("Cloning into a temporary directory...") 123 | sh('git init -q "%s"' % tmp) 124 | msg_trace('os.chdir(%r)' % tmp) 125 | os.chdir(tmp) 126 | sh('git checkout -q --orphan "%s"' % DOC_TARGET_BRANCH) 127 | sh('git commit -qm "Initial commit." --allow-empty') 128 | sh('git remote add origin "%s"' % dir) 129 | sh('git push -q origin gh-pages') 130 | 131 | finally: 132 | msg('Cleaning up...') 133 | msg_trace('os.chdir(%r)' % dir) 134 | os.chdir(dir) 135 | msg_trace('shutil.rmtree(%r)' % tmp) 136 | really_rmtree(tmp) 137 | 138 | msg('%s is ready. Continuing.' % DOC_TARGET_BRANCH) 139 | 140 | def main(): 141 | if sh_eval('git symbolic-ref --short HEAD') != u'master': 142 | msg('Not on master; doing nothing.') 143 | return 0 144 | 145 | # Sanity check: does the doc branch exist at all? 146 | branches = {b[2:].strip() for b in sh_eval('git branch', dont_strip=True).splitlines()} 147 | msg_trace('branches = %r' % branches) 148 | if DOC_TARGET_BRANCH not in branches: 149 | init_doc_branch() 150 | 151 | last_rev = sh_eval('git rev-parse HEAD') 152 | last_msg = sh_eval('git log -1 --pretty=%B') 153 | msg_trace('last_rev = %r' % last_rev) 154 | msg_trace('last_msg = %r' % last_msg) 155 | 156 | dir = os.getcwd() 157 | msg_trace('dir = %r' % dir) 158 | 159 | tmp1 = tempfile.mkdtemp(prefix=TEMP_CHECKOUT_PREFIX) 160 | tmp2 = tempfile.mkdtemp(prefix=TEMP_OUTPUT_PREFIX) 161 | msg_trace('tmp1 = %r' % tmp1) 162 | msg_trace('tmp2 = %r' % tmp2) 163 | 164 | try: 165 | msg("Cloning into a temporary directory...") 166 | sh('git clone -qb "%s" "%s" "%s"' % (DOC_TARGET_BRANCH, dir, tmp1)) 167 | msg_trace('os.chdir(%r)' % tmp1) 168 | os.chdir(tmp1) 169 | sh('git checkout -q master') 170 | 171 | msg("Generating documentation...") 172 | args = '%s --features="%s"' % (DOC_ARGS, DOC_FEATURES) 173 | sh('cargo doc %s' % args) 174 | tmp1_target_doc = '%s/target/doc' % tmp1 175 | msg_trace('shutil.move(%r, %r)' % (tmp1_target_doc, tmp2)) 176 | shutil.move(tmp1_target_doc, tmp2) 177 | 178 | msg('Updating %s...' % DOC_TARGET_BRANCH) 179 | sh('git checkout -q "%s"' % DOC_TARGET_BRANCH) 180 | sh('git clean -dfq') 181 | tmp2_doc = '%s/doc' % tmp2 182 | 183 | msg_trace('copytree(%r, %r)' % (tmp2_doc, './doc')) 184 | copytree(tmp2_doc, './doc') 185 | 186 | msg('Committing changes...') 187 | sh('git add .') 188 | sh('git commit --amend -m "Update docs for %s" -m "%s"' % (last_rev[:7], last_msg)) 189 | 190 | sh('git push -fqu origin "%s"' % DOC_TARGET_BRANCH) 191 | 192 | finally: 193 | msg('Cleaning up...') 194 | msg_trace('os.chdir(%r)' % dir) 195 | os.chdir(dir) 196 | msg_trace('shutil.rmtree(%r)' % tmp2) 197 | really_rmtree(tmp2) 198 | msg_trace('shutil.rmtree(%r)' % tmp1) 199 | really_rmtree(tmp1) 200 | 201 | msg('Publishing...') 202 | sh('git push -f origin "%s"' % DOC_TARGET_BRANCH) 203 | 204 | msg('Done.') 205 | 206 | 207 | if __name__ == '__main__': 208 | sys.exit(main()) 209 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This module defines the various error types that can be produced by a failed conversion. 3 | 4 | In addition, it also defines some extension traits to make working with failable conversions more ergonomic (see the `Unwrap*` traits). 5 | */ 6 | 7 | use std::any::Any; 8 | use ::Error; 9 | use std::fmt::{self, Debug, Display}; 10 | use misc::{Saturated, InvalidSentinel, SignedInfinity}; 11 | 12 | macro_rules! Desc { 13 | ( 14 | ($desc:expr) 15 | pub struct $name:ident<$t:ident> $_body:tt; 16 | ) => { 17 | impl<$t> Display for $name<$t> { 18 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 19 | write!(fmt, $desc) 20 | } 21 | } 22 | 23 | impl<$t> Error for $name<$t> where $t: Any { 24 | fn description(&self) -> &str { 25 | $desc 26 | } 27 | } 28 | }; 29 | } 30 | 31 | macro_rules! DummyDebug { 32 | ( 33 | () pub enum $name:ident<$t:ident> { 34 | $(#[doc=$_doc:tt] $vname:ident($_vpay:ident),)+ 35 | } 36 | ) => { 37 | impl<$t> Debug for $name<$t> { 38 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 39 | let msg = match *self { 40 | $($name::$vname(_) => stringify!($vname),)+ 41 | }; 42 | write!(fmt, concat!(stringify!($name), "::{}(..)"), msg) 43 | } 44 | } 45 | }; 46 | 47 | ( 48 | () pub struct $name:ident<$t:ident>(pub $_pay:ident); 49 | ) => { 50 | impl<$t> Debug for $name<$t> { 51 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 52 | write!(fmt, concat!(stringify!($name), "(..)")) 53 | } 54 | } 55 | }; 56 | } 57 | 58 | macro_rules! EnumDesc { 59 | ( 60 | ($($vname:ident => $vdesc:expr,)+) 61 | pub enum $name:ident $_body:tt 62 | ) => { 63 | impl Display for $name { 64 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 65 | write!(fmt, "{}", 66 | match *self { $($name::$vname => $vdesc,)+ }) 67 | } 68 | } 69 | 70 | impl Error for $name { 71 | fn description(&self) -> &str { 72 | match *self { $($name::$vname => $vdesc,)+ } 73 | } 74 | } 75 | }; 76 | 77 | ( 78 | ($($vname:ident => $vdesc:expr,)+) 79 | pub enum $name:ident<$t:ident> $_body:tt 80 | ) => { 81 | impl<$t> Display for $name<$t> { 82 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 83 | write!(fmt, "{}", 84 | match *self { $($name::$vname(..) => $vdesc,)+ }) 85 | } 86 | } 87 | 88 | impl<$t> Error for $name<$t> where $t: Any { 89 | fn description(&self) -> &str { 90 | match *self { $($name::$vname(..) => $vdesc,)+ } 91 | } 92 | } 93 | }; 94 | } 95 | 96 | macro_rules! FromName { 97 | ( 98 | ($fname:ident) 99 | pub enum $name:ident<$t:ident> $_body:tt 100 | ) => { 101 | impl<$t> From<$fname<$t>> for $name<$t> { 102 | #[inline] 103 | fn from(e: $fname<$t>) -> Self { 104 | $name::$fname(e.into_inner()) 105 | } 106 | } 107 | }; 108 | 109 | ( 110 | ($fname:ident<$t:ident>) 111 | pub enum $name:ident $_body:tt 112 | ) => { 113 | impl<$t> From<$fname<$t>> for $name { 114 | #[inline] 115 | fn from(_: $fname<$t>) -> Self { 116 | $name::$fname 117 | } 118 | } 119 | }; 120 | } 121 | 122 | macro_rules! FromNoError { 123 | ( 124 | () pub enum $name:ident $_body:tt 125 | ) => { 126 | impl From for $name { 127 | #[inline] 128 | fn from(_: NoError) -> Self { 129 | panic!(concat!("cannot convert NoError into ", stringify!($name))) 130 | } 131 | } 132 | }; 133 | 134 | ( 135 | () pub enum $name:ident<$t:ident> $_body:tt 136 | ) => { 137 | impl<$t> From for $name<$t> { 138 | fn from(_: NoError) -> Self { 139 | panic!(concat!("cannot convert NoError into ", stringify!($name))) 140 | } 141 | } 142 | }; 143 | 144 | ( 145 | () pub struct $name:ident<$t:ident> $_body:tt; 146 | ) => { 147 | impl<$t> From for $name<$t> { 148 | fn from(_: NoError) -> Self { 149 | panic!(concat!("cannot convert NoError into ", stringify!($name))) 150 | } 151 | } 152 | }; 153 | } 154 | 155 | macro_rules! FromRemap { 156 | ( 157 | ($from:ident($($vname:ident),+)) 158 | pub enum $name:ident $_body:tt 159 | ) => { 160 | impl From<$from> for $name { 161 | #[inline] 162 | fn from(e: $from) -> Self { 163 | match e { 164 | $($from::$vname => $name::$vname,)+ 165 | } 166 | } 167 | } 168 | }; 169 | 170 | ( 171 | ($from:ident<$t:ident>($($vname:ident),+)) 172 | pub enum $name:ident $_body:tt 173 | ) => { 174 | impl<$t> From<$from<$t>> for $name { 175 | #[inline] 176 | fn from(e: $from<$t>) -> Self { 177 | match e { 178 | $($from::$vname(..) => $name::$vname,)+ 179 | } 180 | } 181 | } 182 | }; 183 | 184 | ( 185 | ($from:ident($($vname:ident),+)) 186 | pub enum $name:ident<$t:ident> $_body:tt 187 | ) => { 188 | impl<$t> From<$from<$t>> for $name<$t> { 189 | #[inline] 190 | fn from(e: $from<$t>) -> Self { 191 | match e { 192 | $($from::$vname(v) => $name::$vname(v),)+ 193 | } 194 | } 195 | } 196 | }; 197 | } 198 | 199 | macro_rules! IntoInner { 200 | ( 201 | () pub enum $name:ident<$t:ident> { 202 | $(#[doc=$_doc:tt] $vname:ident($_vpay:ident),)+ 203 | } 204 | ) => { 205 | impl<$t> $name<$t> { 206 | /// Returns the value stored in this error. 207 | #[inline] 208 | pub fn into_inner(self) -> $t { 209 | match self { $($name::$vname(v))|+ => v } 210 | } 211 | } 212 | }; 213 | 214 | ( 215 | () pub struct $name:ident<$t:ident>(pub $_pay:ident); 216 | ) => { 217 | impl<$t> $name<$t> { 218 | /// Returns the value stored in this error. 219 | #[inline] 220 | pub fn into_inner(self) -> $t { 221 | self.0 222 | } 223 | } 224 | }; 225 | } 226 | 227 | custom_derive!{ 228 | /** 229 | A general error enumeration that subsumes all other conversion errors. 230 | 231 | This exists primarily as a "catch-all" for reliably unifying various different kinds of conversion errors. 232 | */ 233 | #[derive( 234 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, 235 | IntoInner, DummyDebug, FromNoError, 236 | EnumDesc( 237 | NegOverflow => "conversion resulted in negative overflow", 238 | PosOverflow => "conversion resulted in positive overflow", 239 | Unrepresentable => "could not convert unrepresentable value", 240 | ), 241 | FromName(Unrepresentable), 242 | FromName(NegOverflow), 243 | FromName(PosOverflow), 244 | FromRemap(RangeError(NegOverflow, PosOverflow)) 245 | )] 246 | pub enum GeneralError { 247 | /// Input was too negative for the target type. 248 | NegOverflow(T), 249 | 250 | /// Input was too positive for the target type. 251 | PosOverflow(T), 252 | 253 | /// Input was not representable in the target type. 254 | Unrepresentable(T), 255 | } 256 | } 257 | 258 | impl From> for GeneralError { 259 | #[inline] 260 | fn from(e: FloatError) -> GeneralError { 261 | use self::FloatError as F; 262 | use self::GeneralError as G; 263 | match e { 264 | F::NegOverflow(v) => G::NegOverflow(v), 265 | F::PosOverflow(v) => G::PosOverflow(v), 266 | F::NotANumber(v) => G::Unrepresentable(v), 267 | } 268 | } 269 | } 270 | 271 | custom_derive! { 272 | /** 273 | A general error enumeration that subsumes all other conversion errors, but discards all input payloads the errors may be carrying. 274 | 275 | This exists primarily as a "catch-all" for reliably unifying various different kinds of conversion errors, and between different input types. 276 | */ 277 | #[derive( 278 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, 279 | FromNoError, 280 | EnumDesc( 281 | NegOverflow => "conversion resulted in negative overflow", 282 | PosOverflow => "conversion resulted in positive overflow", 283 | Unrepresentable => "could not convert unrepresentable value", 284 | ), 285 | FromName(Unrepresentable), 286 | FromName(NegOverflow), 287 | FromName(PosOverflow), 288 | FromRemap(RangeErrorKind(NegOverflow, PosOverflow)), 289 | FromRemap(RangeError(NegOverflow, PosOverflow)), 290 | FromRemap(GeneralError(NegOverflow, PosOverflow, Unrepresentable)) 291 | )] 292 | pub enum GeneralErrorKind { 293 | /// Input was too negative for the target type. 294 | NegOverflow, 295 | 296 | /// Input was too positive for the target type. 297 | PosOverflow, 298 | 299 | /// Input was not representable in the target type. 300 | Unrepresentable, 301 | } 302 | } 303 | 304 | impl From> for GeneralErrorKind { 305 | #[inline] 306 | fn from(e: FloatError) -> GeneralErrorKind { 307 | use self::FloatError as F; 308 | use self::GeneralErrorKind as G; 309 | match e { 310 | F::NegOverflow(..) => G::NegOverflow, 311 | F::PosOverflow(..) => G::PosOverflow, 312 | F::NotANumber(..) => G::Unrepresentable, 313 | } 314 | } 315 | } 316 | 317 | /** 318 | Indicates that it is not possible for the conversion to fail. 319 | 320 | You can use the [`UnwrapOk::unwrap_ok`](./trait.UnwrapOk.html#tymethod.unwrap_ok) method to discard the (statically impossible) `Err` case from a `Result<_, NoError>`, without using `Result::unwrap` (which is typically viewed as a "code smell"). 321 | */ 322 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] 323 | pub enum NoError {} 324 | 325 | impl Display for NoError { 326 | fn fmt(&self, _: &mut fmt::Formatter) -> Result<(), fmt::Error> { 327 | unreachable!() 328 | } 329 | } 330 | 331 | impl Error for NoError { 332 | fn description(&self) -> &str { 333 | unreachable!() 334 | } 335 | } 336 | 337 | custom_derive! { 338 | /// Indicates that the conversion failed because the value was not representable. 339 | #[derive( 340 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, 341 | IntoInner, DummyDebug, FromNoError, 342 | Desc("could not convert unrepresentable value") 343 | )] 344 | pub struct Unrepresentable(pub T); 345 | } 346 | 347 | custom_derive! { 348 | /// Indicates that the conversion failed due to a negative overflow. 349 | #[derive( 350 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, 351 | IntoInner, DummyDebug, FromNoError, 352 | Desc("conversion resulted in negative overflow") 353 | )] 354 | pub struct NegOverflow(pub T); 355 | } 356 | 357 | custom_derive! { 358 | /// Indicates that the conversion failed due to a positive overflow. 359 | #[derive( 360 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, 361 | IntoInner, DummyDebug, FromNoError, 362 | Desc("conversion resulted in positive overflow") 363 | )] 364 | pub struct PosOverflow(pub T); 365 | } 366 | 367 | custom_derive! { 368 | /** 369 | Indicates that a conversion from a floating point type failed. 370 | */ 371 | #[derive( 372 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, 373 | IntoInner, DummyDebug, FromNoError, 374 | EnumDesc( 375 | NegOverflow => "conversion resulted in negative overflow", 376 | PosOverflow => "conversion resulted in positive overflow", 377 | NotANumber => "conversion target does not support not-a-number", 378 | ), 379 | FromName(NegOverflow), 380 | FromName(PosOverflow), 381 | FromRemap(RangeError(NegOverflow, PosOverflow)) 382 | )] 383 | pub enum FloatError { 384 | /// Input was too negative for the target type. 385 | NegOverflow(T), 386 | 387 | /// Input was too positive for the target type. 388 | PosOverflow(T), 389 | 390 | /// Input was not-a-number, which the target type could not represent. 391 | NotANumber(T), 392 | } 393 | } 394 | 395 | custom_derive! { 396 | /** 397 | Indicates that a conversion failed due to a range error. 398 | */ 399 | #[derive( 400 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, 401 | IntoInner, DummyDebug, FromNoError, 402 | EnumDesc( 403 | NegOverflow => "conversion resulted in negative overflow", 404 | PosOverflow => "conversion resulted in positive overflow", 405 | ), 406 | FromName(NegOverflow), 407 | FromName(PosOverflow) 408 | )] 409 | pub enum RangeError { 410 | /// Input was too negative for the target type. 411 | NegOverflow(T), 412 | 413 | /// Input was too positive the target type. 414 | PosOverflow(T), 415 | } 416 | } 417 | 418 | custom_derive! { 419 | /** 420 | Indicates that a conversion failed due to a range error. 421 | 422 | This is a variant of `RangeError` that does not retain the input value which caused the error. It exists to help unify some utility methods and should not generally be used directly, unless you are targeting the `Unwrap*` traits. 423 | */ 424 | #[derive( 425 | Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, 426 | FromNoError, 427 | EnumDesc( 428 | NegOverflow => "conversion resulted in negative overflow", 429 | PosOverflow => "conversion resulted in positive overflow", 430 | ), 431 | FromName(NegOverflow), 432 | FromName(PosOverflow), 433 | FromRemap(RangeError(NegOverflow, PosOverflow)) 434 | )] 435 | pub enum RangeErrorKind { 436 | /// Input was too negative for the target type. 437 | NegOverflow, 438 | 439 | /// Input was too positive for the target type. 440 | PosOverflow, 441 | } 442 | } 443 | 444 | /** 445 | Saturates a `Result`. 446 | */ 447 | pub trait Saturate { 448 | /// The result of saturating. 449 | type Output; 450 | 451 | /** 452 | Replaces an overflow error with a saturated value. 453 | 454 | Unlike `unwrap_or_saturate`, this method can be used in cases where the `Result` error type can encode failures *other* than overflow and underflow. For example, you cannot saturate a float-to-integer conversion using `unwrap_or_saturate` as the error might be `NotANumber`, which doesn't have a meaningful saturation "direction". 455 | 456 | The output of this method will be a `Result` where the error type *does not* contain overflow conditions. What conditions remain must still be dealt with in some fashion. 457 | */ 458 | fn saturate(self) -> Self::Output; 459 | } 460 | 461 | impl Saturate for Result> 462 | where T: Saturated { 463 | type Output = Result>; 464 | 465 | #[inline] 466 | fn saturate(self) -> Self::Output { 467 | use self::FloatError::*; 468 | match self { 469 | Ok(v) => Ok(v), 470 | Err(NegOverflow(_)) => Ok(T::saturated_min()), 471 | Err(PosOverflow(_)) => Ok(T::saturated_max()), 472 | Err(NotANumber(v)) => Err(Unrepresentable(v)) 473 | } 474 | } 475 | } 476 | 477 | impl Saturate for Result> 478 | where T: Saturated { 479 | type Output = Result; 480 | 481 | #[inline] 482 | fn saturate(self) -> Self::Output { 483 | use self::RangeError::*; 484 | match self { 485 | Ok(v) => Ok(v), 486 | Err(NegOverflow(_)) => Ok(T::saturated_min()), 487 | Err(PosOverflow(_)) => Ok(T::saturated_max()) 488 | } 489 | } 490 | } 491 | 492 | impl Saturate for Result 493 | where T: Saturated { 494 | type Output = Result; 495 | 496 | #[inline] 497 | fn saturate(self) -> Self::Output { 498 | use self::RangeErrorKind::*; 499 | match self { 500 | Ok(v) => Ok(v), 501 | Err(NegOverflow) => Ok(T::saturated_min()), 502 | Err(PosOverflow) => Ok(T::saturated_max()) 503 | } 504 | } 505 | } 506 | 507 | /** 508 | Safely unwrap a `Result` that cannot contain an error. 509 | */ 510 | pub trait UnwrapOk { 511 | /** 512 | Unwraps a `Result` without possibility of failing. 513 | 514 | Technically, this is not necessary; it's provided simply to make user code a little clearer. 515 | */ 516 | fn unwrap_ok(self) -> T; 517 | } 518 | 519 | impl UnwrapOk for Result { 520 | #[inline] 521 | fn unwrap_ok(self) -> T { 522 | match self { 523 | Ok(v) => v, 524 | Err(no_error) => match no_error {}, 525 | } 526 | } 527 | } 528 | 529 | /** 530 | Unwrap a conversion by saturating to infinity. 531 | */ 532 | pub trait UnwrapOrInf { 533 | /// The result of unwrapping. 534 | type Output; 535 | 536 | /** 537 | Either unwraps the successfully converted value, or saturates to infinity in the "direction" of overflow. 538 | */ 539 | fn unwrap_or_inf(self) -> Self::Output; 540 | } 541 | 542 | /** 543 | Unwrap a conversion by replacing a failure with an invalid sentinel value. 544 | */ 545 | pub trait UnwrapOrInvalid { 546 | /// The result of unwrapping. 547 | type Output; 548 | 549 | /** 550 | Either unwraps the successfully converted value, or returns the output type's invalid sentinel value. 551 | */ 552 | fn unwrap_or_invalid(self) -> Self::Output; 553 | } 554 | 555 | /** 556 | Unwrap a conversion by saturating. 557 | */ 558 | pub trait UnwrapOrSaturate { 559 | /// The result of unwrapping. 560 | type Output; 561 | 562 | /** 563 | Either unwraps the successfully converted value, or saturates in the "direction" of overflow. 564 | */ 565 | fn unwrap_or_saturate(self) -> Self::Output; 566 | } 567 | 568 | impl UnwrapOrInf for Result 569 | where T: SignedInfinity, E: Into { 570 | type Output = T; 571 | #[inline] 572 | fn unwrap_or_inf(self) -> T { 573 | use self::RangeErrorKind::*; 574 | match self.map_err(Into::into) { 575 | Ok(v) => v, 576 | Err(NegOverflow) => T::neg_infinity(), 577 | Err(PosOverflow) => T::pos_infinity(), 578 | } 579 | } 580 | } 581 | 582 | impl UnwrapOrInvalid for Result 583 | where T: InvalidSentinel { 584 | type Output = T; 585 | #[inline] 586 | fn unwrap_or_invalid(self) -> T { 587 | match self { 588 | Ok(v) => v, 589 | Err(..) => T::invalid_sentinel(), 590 | } 591 | } 592 | } 593 | 594 | impl UnwrapOrSaturate for Result 595 | where T: Saturated, E: Into { 596 | type Output = T; 597 | #[inline] 598 | fn unwrap_or_saturate(self) -> T { 599 | use self::RangeErrorKind::*; 600 | match self.map_err(Into::into) { 601 | Ok(v) => v, 602 | Err(NegOverflow) => T::saturated_min(), 603 | Err(PosOverflow) => T::saturated_max(), 604 | } 605 | } 606 | } 607 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | macro_rules! max_of { 2 | ($name:ident) => { ::std::$name::MAX }; 3 | } 4 | 5 | macro_rules! min_of { 6 | ($name:ident) => { ::std::$name::MIN }; 7 | } 8 | 9 | macro_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 | 24 | macro_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 | 45 | macro_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 | 63 | macro_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 | 84 | macro_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 | 102 | macro_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 | 116 | macro_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 | 145 | macro_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 | 370 | mod 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 | 394 | #[cfg(feature = "std")] 395 | mod lang_floats { 396 | use {ApproxFrom, ApproxScheme}; 397 | use ValueFrom; 398 | use errors::{NoError, RangeError}; 399 | 400 | // f32 -> f64: strictly widening 401 | impl ApproxFrom for f64 402 | where Scheme: ApproxScheme { 403 | type Err = NoError; 404 | #[inline] 405 | fn approx_from(src: f32) -> Result { 406 | Ok(src as f64) 407 | } 408 | } 409 | 410 | impl ValueFrom for f64 { 411 | type Err = NoError; 412 | #[inline] 413 | fn value_from(src: f32) -> Result { 414 | Ok(src as f64) 415 | } 416 | } 417 | 418 | // f64 -> f32: narrowing, approximate 419 | impl ApproxFrom for f32 { 420 | type Err = RangeError; 421 | #[inline] 422 | fn approx_from(src: f64) -> Result { 423 | if !src.is_finite() { 424 | return Ok(src as f32); 425 | } 426 | if !(::std::f32::MIN as f64 <= src) { 427 | return Err(RangeError::NegOverflow(src)); 428 | } 429 | if !(src <= ::std::f32::MAX as f64) { 430 | return Err(RangeError::PosOverflow(src)); 431 | } 432 | Ok(src as f32) 433 | } 434 | } 435 | } 436 | 437 | #[cfg(feature = "std")] 438 | mod lang_int_to_float { 439 | num_conv! { i8=> w f32, w f64 } 440 | num_conv! { i16=> w f32, w f64 } 441 | num_conv! { i32=> nf [+- 16_777_216] f32, w f64 } 442 | num_conv! { i64=> nf [+- 16_777_216] f32, nf [+- 9_007_199_254_740_992] f64 } 443 | 444 | num_conv! { u8=> w f32, w f64 } 445 | num_conv! { u16=> w f32, w f64 } 446 | num_conv! { u32=> nf [, 16_777_216] f32, w f64 } 447 | num_conv! { u64=> nf [, 16_777_216] f32, nf [, 9_007_199_254_740_992] f64 } 448 | 449 | num_conv! { isize=> nf [+- 16_777_216] f32, 450 | #[32] w f64, #[64] nf [+- 9_007_199_254_740_992] f64 } 451 | num_conv! { usize=> nf [, 16_777_216] f32, 452 | #[32] w f64, #[64] nf [, 9_007_199_254_740_992] f64 } 453 | } 454 | 455 | #[cfg(feature = "std")] 456 | mod lang_float_to_int { 457 | /* 458 | 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. 459 | 460 | *Yeah.* That's floating point for you! 461 | */ 462 | num_conv! { f32=> fan i8, fan i16, 463 | fan [-2.1474836e9, 2.1474835e9] i32, 464 | fan [-9.223372e18, 9.2233715e18] i64 } 465 | num_conv! { f32=> fan u8, fan u16, 466 | fan [0.0, 4.294967e9] u32, 467 | fan [0.0, 1.8446743e19] u64 } 468 | num_conv! { f32=> 469 | #[32] fan [-2.1474836e9, 2.1474835e9] isize, 470 | #[32] fan [0.0, 4.294967e9] usize, 471 | #[64] fan [-9.223372e18, 9.2233715e18] isize, 472 | #[64] fan [0.0, 1.8446743e19] usize } 473 | 474 | num_conv! { f64=> fan i8, fan i16, fan i32, 475 | fan [-9.223372036854776e18, 9.223372036854775e18] i64 } 476 | num_conv! { f64=> fan u8, fan u16, fan u32, 477 | fan [0.0, 1.844674407370955e19] u64 } 478 | num_conv! { f64=> 479 | #[32] fan isize, #[32] fan usize, 480 | #[64] fan [-9.223372036854776e18, 9.223372036854775e18] isize, 481 | #[64] fan [0.0, 1.844674407370955e19] usize } 482 | } 483 | 484 | mod lang_char_to_int { 485 | use TryFrom; 486 | use ValueFrom; 487 | use errors::{NoError, PosOverflow}; 488 | 489 | impl TryFrom for u32 { 490 | type Err = NoError; 491 | #[inline] 492 | fn try_from(src: char) -> Result { 493 | Ok(src as u32) 494 | } 495 | } 496 | 497 | impl TryFrom for usize { 498 | type Err = NoError; 499 | #[inline] 500 | fn try_from(src: char) -> Result { 501 | Ok(src as usize) 502 | } 503 | } 504 | 505 | impl TryFrom for isize { 506 | type Err = NoError; 507 | #[inline] 508 | fn try_from(src: char) -> Result { 509 | Ok(src as isize) 510 | } 511 | } 512 | 513 | macro_rules! conv_char_to_int { 514 | ($($ts:ty),* $(,)*) => { 515 | $( 516 | impl TryFrom for $ts { 517 | type Err = PosOverflow; 518 | #[inline] 519 | fn try_from(src: char) -> Result<$ts, Self::Err> { 520 | <$ts as ValueFrom<_>>::value_from(src as u32) 521 | .map_err(|_| PosOverflow(src)) 522 | } 523 | } 524 | )* 525 | }; 526 | } 527 | 528 | macro_rules! conv_char_to_int_wide { 529 | ($($ts:ty),* $(,)*) => { 530 | $( 531 | impl TryFrom for $ts { 532 | type Err = NoError; 533 | #[inline] 534 | fn try_from(src: char) -> Result<$ts, Self::Err> { 535 | <$ts as ValueFrom<_>>::value_from(src as u32) 536 | } 537 | } 538 | )* 539 | }; 540 | } 541 | 542 | conv_char_to_int! { i8, i16, i32, u8, u16 } 543 | conv_char_to_int_wide! { i64, u64 } 544 | } 545 | 546 | mod lang_int_to_char { 547 | use TryFrom; 548 | use ValueFrom; 549 | use errors::{NoError, Unrepresentable, UnwrapOk}; 550 | 551 | impl TryFrom for char { 552 | type Err = NoError; 553 | #[inline] 554 | fn try_from(src: u8) -> Result { 555 | Ok(src as char) 556 | } 557 | } 558 | impl TryFrom for char { 559 | type Err = Unrepresentable; 560 | #[inline] 561 | fn try_from(src: u16) -> Result { 562 | TryFrom::try_from( 563 | >::value_from(src).unwrap_ok() 564 | ).map_err(|_| Unrepresentable(src)) 565 | } 566 | } 567 | 568 | impl TryFrom for char { 569 | type Err = Unrepresentable; 570 | #[inline] 571 | fn try_from(src: u32) -> Result { 572 | ::std::char::from_u32(src).ok_or_else(|| Unrepresentable(src)) 573 | } 574 | } 575 | 576 | macro_rules! conv_int_to_char { 577 | ($($ts:ty),* $(,)*) => { 578 | $( 579 | impl TryFrom<$ts> for char { 580 | type Err = Unrepresentable<$ts>; 581 | #[inline] 582 | fn try_from(src: $ts) -> Result { 583 | >::value_from(src) 584 | .map_err(|_| Unrepresentable(src)) 585 | .and_then(|usv| TryFrom::try_from(usv) 586 | .map_err(|_| Unrepresentable(src))) 587 | } 588 | } 589 | )* 590 | }; 591 | } 592 | 593 | conv_int_to_char! { i8, i16, i32, i64, isize, u64, usize } 594 | } 595 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This crate provides a number of conversion traits with more specific semantics than those provided by `as` or `From`/`Into`. 3 | 4 | The goal with the traits provided here is to be more specific about what generic code can rely on, as well as provide reasonably self-describing alternatives to the standard `From`/`Into` traits. For example, the although `T: From` might be satisfied, it imposes no restrictions on the *kind* of conversion being implemented. As such, the traits in this crate try to be very specific about what conversions are allowed. This makes them less generally applicable, but more useful where they *do* apply. 5 | 6 | In addition, `From`/`Into` requires all conversions to succeed or panic. All conversion traits in this crate define an associated error type, allowing code to react to failed conversions as appropriate. 7 | 8 | 21 | 30 | 31 | ## Compatibility 32 | 33 | `conv` is compatible with Rust 1.2 and higher. 34 | 35 | ## Change Log 36 | 37 | ### v0.3.4 38 | 39 | - Made `TryFrom!` macro more robust. Thanks to JeffBelgum. 40 | 41 | ### v0.3.3 42 | 43 | - Support for `no_std` via the default `std` feature. 44 | 45 | ### v0.3.2 46 | 47 | - Added integer ↔ `char` conversions. 48 | - Added missing `isize`/`usize` → `f32`/`f64` conversions. 49 | - Fixed the error type of `i64` → `usize` for 64-bit targets. 50 | 51 | ### v0.3.1 52 | 53 | - Change to `unwrap_ok` for better codegen (thanks bluss). 54 | - Fix for Rust breaking change (code in question was dodgy anyway; thanks m4rw3r). 55 | 56 | ### v0.3.0 57 | 58 | - Added an `Error` constraint to all `Err` associated types. This will break any user-defined conversions where the `Err` type does not implement `Error`. 59 | - Renamed the `Overflow` and `Underflow` errors to `PosOverflow` and `NegOverflow` respectively. In the context of floating point conversions, "underflow" usually means the value was too close to zero to correctly represent. 60 | 61 | ### v0.2.1 62 | 63 | - Added `ConvUtil::into_as` as a shortcut for `Into::::into`. 64 | - Added `#[inline]` attributes. 65 | - Added `Saturate::saturate`, which can saturate `Result`s arising from over/underflow. 66 | 67 | ### v0.2.0 68 | 69 | - Changed all error types to include the original input as payload. This breaks pretty much *everything*. Sorry about that. On the bright side, there's now no downside to using the conversion traits for non-`Copy` types. 70 | - Added the normal rounding modes for float → int approximations: `RoundToNearest`, `RoundToNegInf`, `RoundToPosInf`, and `RoundToZero`. 71 | - `ApproxWith` is now subsumed by a pair of extension traits (`ConvUtil` and `ConvAsUtil`), that also have shortcuts for `TryInto` and `ValueInto` so that you can specify the destination type on the method. 72 | 73 | # Overview 74 | 75 | The following traits are used to define various conversion semantics: 76 | 77 | - [`ApproxFrom`](./trait.ApproxFrom.html)/[`ApproxInto`](./trait.ApproxInto.html) - approximate conversions, with selectable approximation scheme (see [`ApproxScheme`](./trait.ApproxScheme.html)). 78 | - [`TryFrom`](./trait.TryFrom.html)/[`TryInto`](./trait.TryInto.html) - general, potentially failing value conversions. 79 | - [`ValueFrom`](./trait.ValueFrom.html)/[`ValueInto`](./trait.ValueInto.html) - exact, value-preserving conversions. 80 | 81 | When *defining* a conversion, try to implement the `*From` trait variant where possible. When *using* a conversion, try to depend on the `*Into` trait variant where possible. This is because the `*Into` traits automatically use `*From` implementations, but not the reverse. Implementing `*From` and using `*Into` ensures conversions work in as many contexts as possible. 82 | 83 | These extension methods are provided to help with some common cases: 84 | 85 | - [`ConvUtil::approx_as`](./trait.ConvUtil.html#method.approx_as) - approximates to `Dst` with the `DefaultApprox` scheme. 86 | - [`ConvUtil::approx_as_by`](./trait.ConvUtil.html#method.approx_as_by) - approximates to `Dst` with the scheme `S`. 87 | - [`ConvUtil::into_as`](./trait.ConvUtil.html#method.into_as) - converts to `Dst` using `Into::into`. 88 | - [`ConvUtil::try_as`](./trait.ConvUtil.html#method.try_as) - converts to `Dst` using `TryInto::try_into`. 89 | - [`ConvUtil::value_as`](./trait.ConvUtil.html#method.value_as) - converts to `Dst` using `ValueInto::value_into`. 90 | - [`ConvAsUtil::approx`](./trait.ConvAsUtil.html#method.approx) - approximates to an inferred destination type with the `DefaultApprox` scheme. 91 | - [`ConvAsUtil::approx_by`](./trait.ConvAsUtil.html#method.approx_by) - approximates to an inferred destination type with the scheme `S`. 92 | - [`Saturate::saturate`](./errors/trait.Saturate.html#tymethod.saturate) - saturates on overflow. 93 | - [`UnwrapOk::unwrap_ok`](./errors/trait.UnwrapOk.html#tymethod.unwrap_ok) - unwraps results from conversions that cannot fail. 94 | - [`UnwrapOrInf::unwrap_or_inf`](./errors/trait.UnwrapOrInf.html#tymethod.unwrap_or_inf) - saturates to ±∞ on failure. 95 | - [`UnwrapOrInvalid::unwrap_or_invalid`](./errors/trait.UnwrapOrInvalid.html#tymethod.unwrap_or_invalid) - substitutes the target type's "invalid" sentinel value on failure. 96 | - [`UnwrapOrSaturate::unwrap_or_saturate`](./errors/trait.UnwrapOrSaturate.html#tymethod.unwrap_or_saturate) - saturates to the maximum or minimum value of the target type on failure. 97 | 98 | A macro is provided to assist in implementing conversions: 99 | 100 | - [`TryFrom!`](./macros/index.html#tryfrom!) - derives an implementation of [`TryFrom`](./trait.TryFrom.html). 101 | 102 | If you are implementing your own types, you may also be interested in the traits contained in the [`misc`](./misc/index.html) module. 103 | 104 | ## Provided Implementations 105 | 106 | The crate provides several blanket implementations: 107 | 108 | - `*From for A` (all types can be converted from and into themselves). 109 | - `*Into for Src where Dst: *From` (`*From` implementations imply a matching `*Into` implementation). 110 | 111 | Conversions for the builtin numeric (integer and floating point) types are provided. In general, `ValueFrom` conversions exist for all pairs except for float → integer (since such a conversion is generally unlikely to *exactly* succeed) and `f64 → f32` (for the same reason). `ApproxFrom` conversions with the `DefaultApprox` scheme exist between all pairs. `ApproxFrom` with the `Wrapping` scheme exist between integers. 112 | 113 | ## Errors 114 | 115 | A number of error types are defined in the [`errors`](./errors/index.html) module. Generally, conversions use whichever error type most *narrowly* defines the kinds of failures that can occur. For example: 116 | 117 | - `ValueFrom for u16` cannot possibly fail, and as such it uses `NoError`. 118 | - `ValueFrom for u16` can *only* fail with a negative overflow, thus it uses the `NegOverflow` type. 119 | - `ValueFrom for u16` can overflow in either direction, hence it uses `RangeError`. 120 | - Finally, `ApproxFrom for u16` can overflow (positive or negative), or attempt to convert NaN; `FloatError` covers those three cases. 121 | 122 | Because there are *numerous* error types, the `GeneralError` enum is provided. `From for GeneralError` exists for each error type `E` defined by this crate (even for `NoError`!), allowing errors to be translated automatically by `try!`. In fact, all errors can be "expanded" to *all* more general forms (*e.g.* `NoError` → `NegOverflow`, `PosOverflow` → `RangeError` → `FloatError`). 123 | 124 | Aside from `NoError`, the various error types wrap the input value that you attempted to convert. This is so that non-`Copy` types do not need to be pre-emptively cloned prior to conversion, just in case the conversion fails. A downside is that this means there are many, *many* incompatible error types. 125 | 126 | To help alleviate this, there is also `GeneralErrorKind`, which is simply `GeneralError` without the payload, and all errors can be converted into it directly. 127 | 128 | The reason for not just using `GeneralErrorKind` in the first place is to statically reduce the number of potential error cases you need to deal with. It also allows the `Unwrap*` extension traits to be defined *without* the possibility for runtime failure (*e.g.* you cannot use `unwrap_or_saturate` with a `FloatError`, because what do you do if the error is `NotANumber`; saturate to max or to min? Or panic?). 129 | 130 | # Examples 131 | 132 | ``` 133 | # extern crate conv; 134 | # use conv::*; 135 | # fn main() { 136 | // This *cannot* fail, so we can use `unwrap_ok` to discard the `Result`. 137 | assert_eq!(u8::value_from(0u8).unwrap_ok(), 0u8); 138 | 139 | // This *can* fail. Specifically, it can overflow toward negative infinity. 140 | assert_eq!(u8::value_from(0i8), Ok(0u8)); 141 | assert_eq!(u8::value_from(-1i8), Err(NegOverflow(-1))); 142 | 143 | // This can overflow in *either* direction; hence the change to `RangeError`. 144 | assert_eq!(u8::value_from(-1i16), Err(RangeError::NegOverflow(-1))); 145 | assert_eq!(u8::value_from(0i16), Ok(0u8)); 146 | assert_eq!(u8::value_from(256i16), Err(RangeError::PosOverflow(256))); 147 | 148 | // We can use the extension traits to simplify this a little. 149 | assert_eq!(u8::value_from(-1i16).unwrap_or_saturate(), 0u8); 150 | assert_eq!(u8::value_from(0i16).unwrap_or_saturate(), 0u8); 151 | assert_eq!(u8::value_from(256i16).unwrap_or_saturate(), 255u8); 152 | 153 | // Obviously, all integers can be "approximated" using the default scheme (it 154 | // doesn't *do* anything), but they can *also* be approximated with the 155 | // `Wrapping` scheme. 156 | assert_eq!( 157 | >::approx_from(400u16), 158 | Err(PosOverflow(400))); 159 | assert_eq!( 160 | >::approx_from(400u16), 161 | Ok(144u8)); 162 | 163 | // This is rather inconvenient; as such, there are a number of convenience 164 | // extension methods available via `ConvUtil` and `ConvAsUtil`. 165 | assert_eq!(400u16.approx(), Err::(PosOverflow(400))); 166 | assert_eq!(400u16.approx_by::(), Ok::(144u8)); 167 | assert_eq!(400u16.approx_as::(), Err(PosOverflow(400))); 168 | assert_eq!(400u16.approx_as_by::(), Ok(144)); 169 | 170 | # #[cfg(feature = "std")] fn std_0() { 171 | // Integer -> float conversions *can* fail due to limited precision. 172 | // Once the continuous range of exactly representable integers is exceeded, the 173 | // provided implementations fail with overflow errors. 174 | assert_eq!(f32::value_from(16_777_216i32), Ok(16_777_216.0f32)); 175 | assert_eq!(f32::value_from(16_777_217i32), Err(RangeError::PosOverflow(16_777_217))); 176 | 177 | // Float -> integer conversions have to be done using approximations. Although 178 | // exact conversions are *possible*, "advertising" this with an implementation 179 | // is misleading. 180 | // 181 | // Note that `DefaultApprox` for float -> integer uses whatever rounding 182 | // mode is currently active (*i.e.* whatever `as` would do). 183 | assert_eq!(41.0f32.approx(), Ok(41u8)); 184 | assert_eq!(41.3f32.approx(), Ok(41u8)); 185 | assert_eq!(41.5f32.approx(), Ok(41u8)); 186 | assert_eq!(41.8f32.approx(), Ok(41u8)); 187 | assert_eq!(42.0f32.approx(), Ok(42u8)); 188 | 189 | assert_eq!(255.0f32.approx(), Ok(255u8)); 190 | assert_eq!(256.0f32.approx(), Err::(FloatError::PosOverflow(256.0))); 191 | 192 | // Sometimes, it can be useful to saturate the conversion from float to 193 | // integer directly, then account for NaN as input separately. The `Saturate` 194 | // extension trait exists for this reason. 195 | assert_eq!((-23.0f32).approx_as::().saturate(), Ok(0)); 196 | assert_eq!(302.0f32.approx_as::().saturate(), Ok(255u8)); 197 | assert!(std::f32::NAN.approx_as::().saturate().is_err()); 198 | # } 199 | # #[cfg(not(feature = "std"))] fn std_0() {} 200 | # std_0(); 201 | 202 | // If you really don't care about the specific kind of error, you can just rely 203 | // on automatic conversion to `GeneralErrorKind`. 204 | fn too_many_errors() -> Result<(), GeneralErrorKind> { 205 | assert_eq!({let r: u8 = try!(0u8.value_into()); r}, 0u8); 206 | assert_eq!({let r: u8 = try!(0i8.value_into()); r}, 0u8); 207 | assert_eq!({let r: u8 = try!(0i16.value_into()); r}, 0u8); 208 | # #[cfg(feature = "std")] fn std_1() -> Result<(), GeneralErrorKind> { 209 | assert_eq!({let r: u8 = try!(0.0f32.approx()); r}, 0u8); 210 | # Ok(()) 211 | # } 212 | # #[cfg(not(feature = "std"))] fn std_1() -> Result<(), GeneralErrorKind> { 213 | # Ok(()) 214 | # } 215 | # try!(std_1()); 216 | Ok(()) 217 | } 218 | # let _ = too_many_errors(); 219 | # } 220 | ``` 221 | 222 | */ 223 | 224 | #![deny(missing_docs)] 225 | 226 | #![cfg_attr(not(feature = "std"), no_std)] 227 | #[cfg(not(feature = "std"))] extern crate core as std; 228 | 229 | #[macro_use] extern crate custom_derive; 230 | 231 | // Exported macros. 232 | pub mod macros; 233 | 234 | pub use errors::{ 235 | NoError, GeneralError, GeneralErrorKind, Unrepresentable, 236 | NegOverflow, PosOverflow, 237 | FloatError, RangeError, RangeErrorKind, 238 | Saturate, 239 | UnwrapOk, UnwrapOrInf, UnwrapOrInvalid, UnwrapOrSaturate, 240 | }; 241 | 242 | #[cfg(not(feature = "std"))] 243 | /** 244 | A conversion error. Corresponds to std::error:Error. 245 | */ 246 | pub trait Error: core::fmt::Debug + core::fmt::Display + core::any::Any { 247 | /// A short description of the error 248 | fn description(&self) -> &str; 249 | } 250 | 251 | #[cfg(feature = "std")] 252 | pub use std::error::Error; 253 | 254 | /** 255 | Publicly re-exports the most generally useful set of items. 256 | 257 | Usage of the prelude should be considered **unstable**. Although items will likely *not* be removed without bumping the major version, new items *may* be added, which could potentially cause name conflicts in user code. 258 | */ 259 | pub mod prelude { 260 | pub use super::{ 261 | ApproxFrom, ApproxInto, 262 | ValueFrom, ValueInto, 263 | GeneralError, GeneralErrorKind, 264 | Saturate, 265 | UnwrapOk, UnwrapOrInf, UnwrapOrInvalid, UnwrapOrSaturate, 266 | ConvUtil, ConvAsUtil, 267 | RoundToNearest, RoundToZero, Wrapping, 268 | }; 269 | } 270 | 271 | macro_rules! as_item { 272 | ($($i:item)*) => {$($i)*}; 273 | } 274 | 275 | macro_rules! item_for_each { 276 | ( 277 | $( ($($arg:tt)*) ),* $(,)* => { $($exp:tt)* } 278 | ) => { 279 | macro_rules! body { 280 | $($exp)* 281 | } 282 | 283 | $( 284 | body! { $($arg)* } 285 | )* 286 | }; 287 | } 288 | 289 | pub mod errors; 290 | pub mod misc; 291 | 292 | mod impls; 293 | 294 | /** 295 | This trait is used to perform a conversion that is permitted to approximate the result, but *not* to wrap or saturate the result to fit into the destination type's representable range. 296 | 297 | Where possible, prefer *implementing* this trait over `ApproxInto`, but prefer *using* `ApproxInto` for generic constraints. 298 | 299 | # Details 300 | 301 | All implementations of this trait must provide a conversion that can be separated into two logical steps: an approximation transform, and a representation transform. 302 | 303 | The "approximation transform" step involves transforming the input value into an approximately equivalent value which is supported by the target type *without* taking the target type's representable range into account. For example, this might involve rounding or truncating a floating point value to an integer, or reducing the accuracy of a floating point value. 304 | 305 | The "representation transform" step *exactly* rewrites the value from the source type's binary representation into the destination type's binary representation. This step *may not* transform the value in any way. If the result of the approximation is not representable, the conversion *must* fail. 306 | 307 | The major reason for this formulation is to exactly define what happens when converting between floating point and integer types. Often, it is unclear what happens to floating point values beyond the range of the target integer type. Do they saturate, wrap, or cause a failure? 308 | 309 | With this formulation, it is well-defined: if a floating point value is outside the representable range, the conversion fails. This allows users to distinguish between approximation and range violation, and act accordingly. 310 | */ 311 | pub trait ApproxFrom: Sized where Scheme: ApproxScheme { 312 | /// The error type produced by a failed conversion. 313 | type Err: Error; 314 | 315 | /// Convert the given value into an approximately equivalent representation. 316 | fn approx_from(src: Src) -> Result; 317 | } 318 | 319 | impl ApproxFrom for Src where Scheme: ApproxScheme { 320 | type Err = NoError; 321 | fn approx_from(src: Src) -> Result { 322 | Ok(src) 323 | } 324 | } 325 | 326 | /** 327 | This is the dual of `ApproxFrom`; see that trait for information. 328 | 329 | Where possible, prefer *using* this trait over `ApproxFrom` for generic constraints, but prefer *implementing* `ApproxFrom`. 330 | */ 331 | pub trait ApproxInto where Scheme: ApproxScheme { 332 | /// The error type produced by a failed conversion. 333 | type Err: Error; 334 | 335 | /// Convert the subject into an approximately equivalent representation. 336 | fn approx_into(self) -> Result; 337 | } 338 | 339 | impl ApproxInto for Src 340 | where 341 | Dst: ApproxFrom, 342 | Scheme: ApproxScheme, 343 | { 344 | type Err = Dst::Err; 345 | fn approx_into(self) -> Result { 346 | ApproxFrom::approx_from(self) 347 | } 348 | } 349 | 350 | /** 351 | This trait is used to mark approximation scheme types. 352 | */ 353 | pub trait ApproxScheme {} 354 | 355 | /** 356 | The "default" approximation scheme. This scheme does whatever would generally be expected of a lossy conversion, assuming no additional context or instruction is given. 357 | 358 | This is a double-edged sword: it has the loosest semantics, but is far more likely to exist than more complicated approximation schemes. 359 | */ 360 | pub enum DefaultApprox {} 361 | impl ApproxScheme for DefaultApprox {} 362 | 363 | /** 364 | This scheme is used to convert a value by "wrapping" it into a narrower range. 365 | 366 | In abstract, this can be viewed as the opposite of rounding: rather than preserving the most significant bits of a value, it preserves the *least* significant bits of a value. 367 | */ 368 | pub enum Wrapping {} 369 | impl ApproxScheme for Wrapping {} 370 | 371 | /** 372 | This scheme is used to convert a value by rounding it to the nearest representable value, with ties rounding away from zero. 373 | */ 374 | pub enum RoundToNearest {} 375 | impl ApproxScheme for RoundToNearest {} 376 | 377 | /** 378 | This scheme is used to convert a value by rounding it toward negative infinity to the nearest representable value. 379 | */ 380 | pub enum RoundToNegInf {} 381 | impl ApproxScheme for RoundToNegInf {} 382 | 383 | /** 384 | This scheme is used to convert a value by rounding it toward positive infinity to the nearest representable value. 385 | */ 386 | pub enum RoundToPosInf {} 387 | impl ApproxScheme for RoundToPosInf {} 388 | 389 | /** 390 | This scheme is used to convert a value by rounding it toward zero to the nearest representable value. 391 | */ 392 | pub enum RoundToZero {} 393 | impl ApproxScheme for RoundToZero {} 394 | 395 | /** 396 | This trait is used to perform a conversion between different semantic types which might fail. 397 | 398 | Where possible, prefer *implementing* this trait over `TryInto`, but prefer *using* `TryInto` for generic constraints. 399 | 400 | # Details 401 | 402 | Typically, this should be used in cases where you are converting between values whose ranges and/or representations only partially overlap. That the conversion may fail should be a reasonably expected outcome. A standard example of this is converting from integers to enums of unitary variants. 403 | */ 404 | pub trait TryFrom: Sized { 405 | /// The error type produced by a failed conversion. 406 | type Err: Error; 407 | 408 | /// Convert the given value into the subject type. 409 | fn try_from(src: Src) -> Result; 410 | } 411 | 412 | impl TryFrom for Src { 413 | type Err = NoError; 414 | fn try_from(src: Src) -> Result { 415 | Ok(src) 416 | } 417 | } 418 | 419 | /** 420 | This is the dual of `TryFrom`; see that trait for information. 421 | 422 | Where possible, prefer *using* this trait over `TryFrom` for generic constraints, but prefer *implementing* `TryFrom`. 423 | */ 424 | pub trait TryInto { 425 | /// The error type produced by a failed conversion. 426 | type Err: Error; 427 | 428 | /// Convert the subject into the destination type. 429 | fn try_into(self) -> Result; 430 | } 431 | 432 | impl TryInto for Src where Dst: TryFrom { 433 | type Err = Dst::Err; 434 | fn try_into(self) -> Result { 435 | TryFrom::try_from(self) 436 | } 437 | } 438 | 439 | /** 440 | This trait is used to perform an exact, value-preserving conversion. 441 | 442 | Where possible, prefer *implementing* this trait over `ValueInto`, but prefer *using* `ValueInto` for generic constraints. 443 | 444 | # Details 445 | 446 | Implementations of this trait should be reflexive, associative and commutative (in the absence of conversion errors). That is, all possible cycles of `ValueFrom` conversions (for which each "step" has a defined implementation) should produce the same result, with a given value either being "round-tripped" exactly, or an error being produced. 447 | */ 448 | pub trait ValueFrom: Sized { 449 | /// The error type produced by a failed conversion. 450 | type Err: Error; 451 | 452 | /// Convert the given value into an exactly equivalent representation. 453 | fn value_from(src: Src) -> Result; 454 | } 455 | 456 | impl ValueFrom for Src { 457 | type Err = NoError; 458 | fn value_from(src: Src) -> Result { 459 | Ok(src) 460 | } 461 | } 462 | 463 | /** 464 | This is the dual of `ValueFrom`; see that trait for information. 465 | 466 | Where possible, prefer *using* this trait over `ValueFrom` for generic constraints, but prefer *implementing* `ValueFrom`. 467 | */ 468 | pub trait ValueInto { 469 | /// The error type produced by a failed conversion. 470 | type Err: Error; 471 | 472 | /// Convert the subject into an exactly equivalent representation. 473 | fn value_into(self) -> Result; 474 | } 475 | 476 | impl ValueInto for Src where Dst: ValueFrom { 477 | type Err = Dst::Err; 478 | fn value_into(self) -> Result { 479 | ValueFrom::value_from(self) 480 | } 481 | } 482 | 483 | /** 484 | This extension trait exists to simplify using various conversions. 485 | 486 | If there is more than one implementation for a given type/trait pair, a simple call to `*_into` may not be uniquely resolvable. Due to the position of the type parameter (on the trait itself), it is cumbersome to specify the destination type. A similar problem exists for approximation schemes. 487 | 488 | See also the [`ConvAsUtil`](./trait.ConvAsUtil.html) trait. 489 | 490 | > **Note**: There appears to be a bug in `rustdoc`'s output. This trait is implemented *for all* types, though the methods are only available for types where the appropriate conversions are defined. 491 | */ 492 | pub trait ConvUtil { 493 | /// Approximate the subject to a given type with the default scheme. 494 | fn approx_as(self) -> Result 495 | where Self: Sized + ApproxInto { 496 | self.approx_into() 497 | } 498 | 499 | /// Approximate the subject to a given type with a specific scheme. 500 | fn approx_as_by(self) -> Result 501 | where 502 | Self: Sized + ApproxInto, 503 | Scheme: ApproxScheme, 504 | { 505 | self.approx_into() 506 | } 507 | 508 | /// Convert the subject to a given type. 509 | fn into_as(self) -> Dst 510 | where Self: Sized + Into { 511 | self.into() 512 | } 513 | 514 | /// Attempt to convert the subject to a given type. 515 | fn try_as(self) -> Result 516 | where Self: Sized + TryInto { 517 | self.try_into() 518 | } 519 | 520 | /// Attempt a value conversion of the subject to a given type. 521 | fn value_as(self) -> Result 522 | where Self: Sized + ValueInto { 523 | self.value_into() 524 | } 525 | } 526 | 527 | impl ConvUtil for T {} 528 | 529 | /** 530 | This extension trait exists to simplify using various conversions. 531 | 532 | If there is more than one `ApproxFrom` implementation for a given type, a simple call to `approx_into` may not be uniquely resolvable. Due to the position of the scheme parameter (on the trait itself), it is cumbersome to specify which scheme you wanted. 533 | 534 | The destination type is inferred from context. 535 | 536 | See also the [`ConvUtil`](./trait.ConvUtil.html) trait. 537 | 538 | > **Note**: There appears to be a bug in `rustdoc`'s output. This trait is implemented *for all* types, though the methods are only available for types where the appropriate conversions are defined. 539 | */ 540 | pub trait ConvAsUtil { 541 | /// Approximate the subject with the default scheme. 542 | fn approx(self) -> Result 543 | where Self: Sized + ApproxInto { 544 | self.approx_into() 545 | } 546 | 547 | /// Approximate the subject with a specific scheme. 548 | fn approx_by(self) -> Result 549 | where 550 | Self: Sized + ApproxInto, 551 | Scheme: ApproxScheme, 552 | { 553 | self.approx_into() 554 | } 555 | } 556 | 557 | impl ConvAsUtil for T {} 558 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This module provides convenience macros to help with implementing the conversion traits. 3 | 4 | # `TryFrom!` 5 | 6 | ```ignore 7 | macro_rules! TryFrom { 8 | (($target:ty) $enum:item) => { ... }; 9 | } 10 | ``` 11 | 12 | This macro attempts to derive an implementation of the [`TryFrom`](../trait.TryFrom.html) trait. Specifically, it supports `enum`s consisting entirely of unitary variants, with or without explicit values. The source type can be any integer type which the variants of the enumeration can be explicitly cast to (*i.e.* using `as`). 13 | 14 | If a conversion fails (due to there being no matching variant for the specified integer value `src`), then the conversion returns `Err(Unrepresentable(src))` (see [`Unrepresentable`](../errors/struct.Unrepresentable.html)). 15 | 16 | It is compatible with the [`custom_derive!`](https://crates.io/crates/custom_derive) macro. 17 | 18 | ## Example 19 | 20 | Using `custom_derive!`: 21 | 22 | ``` 23 | #[macro_use] extern crate conv; 24 | #[macro_use] extern crate custom_derive; 25 | 26 | custom_derive! { 27 | #[derive(Debug, PartialEq, TryFrom(i32))] 28 | enum Colours { 29 | Red = 0, 30 | Green = 5, 31 | Blue 32 | } 33 | } 34 | 35 | fn main() { 36 | use conv::{TryFrom, Unrepresentable}; 37 | 38 | assert_eq!(Colours::try_from(0), Ok(Colours::Red)); 39 | assert_eq!(Colours::try_from(1), Err(Unrepresentable(1))); 40 | assert_eq!(Colours::try_from(5), Ok(Colours::Green)); 41 | assert_eq!(Colours::try_from(6), Ok(Colours::Blue)); 42 | assert_eq!(Colours::try_from(7), Err(Unrepresentable(7))); 43 | } 44 | ``` 45 | 46 | The above is equivalent to the following: 47 | 48 | ``` 49 | #[macro_use] extern crate conv; 50 | 51 | #[derive(Debug, PartialEq)] 52 | enum Colours { 53 | Red = 0, 54 | Green = 5, 55 | Blue 56 | } 57 | 58 | TryFrom! { (i32) enum Colours { 59 | Red = 0, 60 | Green = 5, 61 | Blue 62 | } } 63 | # fn main() {} 64 | ``` 65 | */ 66 | 67 | /** 68 | This module contains symbols that we need to publicly re-export for macros, but don't want to show up as part of the public API. 69 | 70 | This module has **excluded** from semver guarantees. 71 | */ 72 | #[doc(hidden)] 73 | pub mod re_export { 74 | pub use ::std::result::Result::{self, Ok}; 75 | } 76 | 77 | /** 78 | See the documentation for the [`macros`](./macros/index.html#tryfrom!) module for details. 79 | */ 80 | #[macro_export] 81 | macro_rules! TryFrom { 82 | (($prim:ty) $(pub)* enum $name:ident { $($body:tt)* }) => { 83 | TryFrom! { 84 | @collect_variants ($name, $prim), 85 | ($($body)*,) -> () 86 | } 87 | }; 88 | 89 | ( 90 | @collect_variants ($name:ident, $prim:ty), 91 | ($(,)*) -> ($($var_names:ident,)*) 92 | ) => { 93 | impl $crate::TryFrom<$prim> for $name { 94 | type Err = $crate::errors::Unrepresentable<$prim>; 95 | fn try_from(src: $prim) -> $crate::macros::re_export::Result<$name, Self::Err> { 96 | $( 97 | if src == $name::$var_names as $prim { 98 | return $crate::macros::re_export::Ok($name::$var_names); 99 | } 100 | )* 101 | Err($crate::errors::Unrepresentable(src)) 102 | } 103 | } 104 | }; 105 | 106 | ( 107 | @collect_variants $fixed:tt, 108 | (#[$_attr:meta] $($tail:tt)*) -> $var_names:tt 109 | ) => { 110 | TryFrom! { 111 | @skip_meta $fixed, 112 | ($($tail)*) -> $var_names 113 | } 114 | }; 115 | 116 | ( 117 | @collect_variants $fixed:tt, 118 | ($var:ident $(= $_val:expr)*, $($tail:tt)*) -> ($($var_names:tt)*) 119 | ) => { 120 | TryFrom! { 121 | @collect_variants $fixed, 122 | ($($tail)*) -> ($($var_names)* $var,) 123 | } 124 | }; 125 | 126 | ( 127 | @collect_variants ($name:ident), 128 | ($var:ident $_struct:tt, $($tail:tt)*) -> ($($var_names:tt)*) 129 | ) => { 130 | const _error: () = concat!( 131 | "cannot derive TryFrom for ", 132 | stringify!($name), 133 | ", due to non-unitary variant ", 134 | stringify!($var), 135 | "." 136 | ); 137 | }; 138 | 139 | ( 140 | @skip_meta $fixed:tt, 141 | (#[$_attr:meta] $($tail:tt)*) -> $var_names:tt 142 | ) => { 143 | TryFrom! { 144 | @skip_meta $fixed, 145 | ($($tail)*) -> $var_names 146 | } 147 | }; 148 | 149 | ( 150 | @skip_meta $fixed:tt, 151 | ($var:ident $($tail:tt)*) -> $var_names:tt 152 | ) => { 153 | TryFrom! { 154 | @collect_variants $fixed, 155 | ($var $($tail)*) -> $var_names 156 | } 157 | }; 158 | } 159 | -------------------------------------------------------------------------------- /src/misc.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This module defines some additional traits not *directly* tied to conversions. 3 | */ 4 | 5 | /** 6 | This trait indicates that values of a type can be logically "saturated". 7 | 8 | This is used by the `errors::UnwrapOrSaturate` extension trait. 9 | */ 10 | pub trait Saturated { 11 | /// Returns the type's saturated, maximum value. 12 | fn saturated_max() -> Self; 13 | 14 | /// Returns the type's saturated, minimum value. 15 | fn saturated_min() -> Self; 16 | } 17 | 18 | item_for_each! { 19 | (i8), (i16), (i32), (i64), (u8), (u16), (u32), (u64), (isize), (usize) => { 20 | ($ity:ident) => { 21 | impl Saturated for $ity { 22 | #[inline] fn saturated_max() -> Self { ::std::$ity::MAX } 23 | #[inline] fn saturated_min() -> Self { ::std::$ity::MIN } 24 | } 25 | }; 26 | } 27 | } 28 | 29 | /** 30 | This trait indicates that a type has an "invalid" sentinel value. 31 | 32 | This is used by the `errors::UnwrapOrInvalid` extension trait. 33 | */ 34 | pub trait InvalidSentinel { 35 | /// Returns the type's "invalid" sentinel value. 36 | fn invalid_sentinel() -> Self; 37 | } 38 | 39 | #[cfg(feature = "std")] 40 | item_for_each! { 41 | (f32), (f64) => { 42 | ($ity:ident) => { 43 | impl InvalidSentinel for $ity { 44 | #[inline] fn invalid_sentinel() -> Self { ::std::$ity::NAN } 45 | } 46 | }; 47 | } 48 | } 49 | 50 | /** 51 | This trait indicates that a type has positive and negative "infinity" values. 52 | 53 | This is used by the `errors::UnwrapOrInf` extension trait. 54 | */ 55 | pub trait SignedInfinity { 56 | /// Returns the type's positive infinity value. 57 | fn neg_infinity() -> Self; 58 | 59 | /// Returns the type's negative infinity value. 60 | fn pos_infinity() -> Self; 61 | } 62 | 63 | #[cfg(feature = "std")] 64 | item_for_each! { 65 | (f32), (f64) => { 66 | ($ity:ident) => { 67 | impl SignedInfinity for $ity { 68 | #[inline] fn neg_infinity() -> Self { ::std::$ity::NEG_INFINITY } 69 | #[inline] fn pos_infinity() -> Self { ::std::$ity::INFINITY } 70 | } 71 | }; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/conv_utils.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate conv; 2 | 3 | use conv::prelude::*; 4 | 5 | #[cfg(feature = "std")] 6 | #[test] 7 | fn test_approx() { 8 | use conv::DefaultApprox; 9 | assert_eq!((1.5f32).approx(), Ok(1i32)); 10 | assert_eq!((1.5f32).approx_by::(), Ok(1)); 11 | assert_eq!((1.5f32).approx_as::(), Ok(1)); 12 | assert_eq!((1.5f32).approx_as_by::(), Ok(1)); 13 | } 14 | 15 | #[test] 16 | fn test_into() { 17 | let v = "ABC".into_as::>(); 18 | assert_eq!(&*v, &[0x41, 0x42, 0x43]); 19 | } 20 | 21 | #[test] 22 | fn test_try() { 23 | #[derive(PartialEq, Debug)] enum ItAintRight { BabeNo, NoNo } 24 | TryFrom! { (u8) enum ItAintRight { BabeNo, NoNo } } 25 | 26 | assert_eq!(0u8.try_as::(), Ok(ItAintRight::BabeNo)); 27 | assert_eq!(1u8.try_as::(), Ok(ItAintRight::NoNo)); 28 | assert_eq!(2u8.try_as::(), Err(conv::Unrepresentable(2))); 29 | } 30 | 31 | #[test] 32 | fn test_value() { 33 | assert_eq!((123u32).value_as::(), Ok(123)); 34 | } 35 | 36 | #[cfg(feature = "std")] 37 | #[test] 38 | fn test_whizzo() { 39 | use conv::errors::Unrepresentable; 40 | assert_eq!((-1.0f32).approx_as::().saturate(), Ok::<_, Unrepresentable<_>>(0u8)); 41 | assert_eq!((-1i32).value_as::().saturate().unwrap_ok(), 0u8); 42 | } 43 | -------------------------------------------------------------------------------- /tests/derive_try_from.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate conv; 2 | 3 | use conv::{TryFrom, Unrepresentable}; 4 | 5 | #[derive(Debug, PartialEq)] 6 | enum Get { Up, Down, AllAround } 7 | 8 | TryFrom! { (u8) 9 | enum Get { 10 | Up, 11 | /// And 12 | Down, 13 | /** And */ 14 | AllAround 15 | } 16 | } 17 | 18 | #[derive(Debug, PartialEq)] 19 | enum GottaGo { GetAway, Fast = 9000, Faster = 9001 } 20 | 21 | TryFrom! { (u16) 22 | enum GottaGo { 23 | GetAway, 24 | Fast = 9000, 25 | /// This show was stupid. 26 | Faster = 9001 27 | } 28 | } 29 | 30 | #[test] 31 | fn test_try_from() { 32 | assert_eq!(Get::try_from(0u8), Ok(Get::Up)); 33 | assert_eq!(Get::try_from(1u8), Ok(Get::Down)); 34 | assert_eq!(Get::try_from(2u8), Ok(Get::AllAround)); 35 | assert_eq!(Get::try_from(3u8), Err(Unrepresentable(3u8))); 36 | 37 | assert_eq!(GottaGo::try_from(0u16), Ok(GottaGo::GetAway)); 38 | assert_eq!(GottaGo::try_from(1u16), Err(Unrepresentable(1u16))); 39 | assert_eq!(GottaGo::try_from(2u16), Err(Unrepresentable(2u16))); 40 | assert_eq!(GottaGo::try_from(3u16), Err(Unrepresentable(3u16))); 41 | assert_eq!(GottaGo::try_from(8999u16), Err(Unrepresentable(8999u16))); 42 | assert_eq!(GottaGo::try_from(9000u16), Ok(GottaGo::Fast)); 43 | assert_eq!(GottaGo::try_from(9001u16), Ok(GottaGo::Faster)); 44 | assert_eq!(GottaGo::try_from(9002u16), Err(Unrepresentable(9002u16))); 45 | } 46 | -------------------------------------------------------------------------------- /tests/lang_char.rs: -------------------------------------------------------------------------------- 1 | extern crate conv; 2 | 3 | #[macro_use] mod util; 4 | 5 | use conv::*; 6 | 7 | use conv::PosOverflow as Of; 8 | use conv::Unrepresentable as Ur; 9 | 10 | macro_rules! check { 11 | (@ $from:ty, $to:ty=> $(;)*) => {}; 12 | 13 | (@ $from:ty, $to:ty=> try cident; $($tail:tt)*) => { 14 | check!(@ $from, $to=> try v: '\x00';); 15 | check!(@ $from, $to=> try v: '\x01';); 16 | check!(@ $from, $to=> $($tail)*); 17 | }; 18 | 19 | (@ $from:ty, $to:ty=> try uident; $($tail:tt)*) => { 20 | check!(@ $from, $to=> try v: 0;); 21 | check!(@ $from, $to=> try v: 1;); 22 | check!(@ $from, $to=> $($tail)*); 23 | }; 24 | 25 | (@ $from:ty, $to:ty=> try v: $src:expr, !$dst:expr; $($tail:tt)*) => { 26 | { 27 | let src: $from = $src; 28 | let dst: Result<$to, _> = src.try_into(); 29 | assert_eq!(dst, Err($dst(src))); 30 | } 31 | check!(@ $from, $to=> $($tail)*); 32 | }; 33 | 34 | (@ $from:ty, $to:ty=> try v: $src:expr; $($tail:tt)*) => { 35 | { 36 | let src: $from = $src; 37 | let dst: Result<$to, _> = src.try_into(); 38 | assert_eq!(dst, Ok($src as $to)); 39 | } 40 | check!(@ $from, $to=> $($tail)*); 41 | }; 42 | 43 | (@ $from:ty, $to:ty=> qt: *; $($tail:tt)*) => { 44 | { 45 | extern crate quickcheck; 46 | 47 | fn property(v: $from) -> bool { 48 | let dst: Result<$to, _> = v.try_into(); 49 | dst == Ok(v as $to) 50 | } 51 | 52 | let mut qc = quickcheck::QuickCheck::new(); 53 | match qc.quicktest(property as fn($from) -> bool) { 54 | Ok(_) => (), 55 | Err(err) => panic!("qv {:?}", err) 56 | } 57 | } 58 | check!(@ $from, $to=> $($tail)*); 59 | }; 60 | 61 | ($from:ty, $to:ty=> $($tail:tt)*) => { 62 | check! { @ $from, $to=> $($tail)*; } 63 | }; 64 | } 65 | 66 | #[test] 67 | fn test_i_to_c() { 68 | check!(u8, char => try uident; qt: *); 69 | 70 | /* 71 | `char` is a pain because `u8` is the *only* type you can cast directly from. So, the `check!` macro is *basically useless*. 72 | 73 | Also, `char` has a great big hole in the middle, which makes things more interesting. 74 | 75 | Instead, we're just going to make sure that the conversions *exist* and have the expected error type. 76 | */ 77 | macro_rules! check_i_to_c { 78 | ($($ts:ty),* $(,)*) => { 79 | $( 80 | { 81 | let v: $ts = 0; 82 | let r: Result> = TryFrom::try_from(v); 83 | assert_eq!(r, Ok('\x00')); 84 | } 85 | )* 86 | }; 87 | } 88 | check_i_to_c!(i8, i16, i32, i64, isize, u16, u32, u64, usize); 89 | } 90 | 91 | #[test] 92 | fn test_c_to_i() { 93 | check!(char, i8=> try cident; 94 | try v: '\u{80}', !Of; 95 | ); 96 | check!(char, i16=> try cident; 97 | try v: '\u{8000}', !Of; 98 | ); 99 | check!(char, i32=> try cident;); 100 | check!(char, i64=> try cident;); 101 | check!(char, u8=> try cident; 102 | try v: '\u{100}', !Of; 103 | ); 104 | check!(char, u16=> try cident; 105 | try v: '\u{10000}', !Of; 106 | ); 107 | check!(char, u32=> try cident;); 108 | check!(char, u64=> try cident;); 109 | for_bitness! { 110 | 32 { 111 | check!(char, isize=> try cident; 112 | try v: '\u{10ffff}'; 113 | ); 114 | check!(char, usize=> try cident;); 115 | } 116 | 64 { 117 | check!(char, i64=> try cident;); 118 | check!(char, u64=> try cident;); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /tests/lang_floats.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "std")] 2 | extern crate conv; 3 | 4 | #[macro_use] mod util; 5 | 6 | use conv::*; 7 | 8 | use conv::FloatError::NegOverflow as FU; 9 | use conv::FloatError::PosOverflow as FO; 10 | 11 | #[test] 12 | fn test_f32() { 13 | check!(f32, f32=> fident; qv: *;); 14 | check!(f32, f64=> fident; qv: *;); 15 | } 16 | 17 | #[test] 18 | fn test_f32_to_int() { 19 | check!(f32, i8=> sidenta; qa: i8=> a: -129.0, !FU; a: 128.0, !FO;); 20 | check!(f32, i16=> sidenta; qa: i16=> a: -32_769.0, !FU; a: 32_768.0, !FO;); 21 | check!(f32, i32=> sidenta; qa: i32=> 22 | a: -2.1474836e9, -2147483648; a: 2.1474835e9, 2147483520; 23 | a: -2_147_500_000.0, !FU; a: 2_147_500_000.0, !FO;); 24 | check!(f32, i64=> sidenta; qa: i64=> 25 | a: -9.223372e18, -9223372036854775808; a: 9.2233715e18, 9223371487098961920; 26 | a: -9_223_373_000_000_000_000.0, !FU; a: 9_223_373_000_000_000_000.0, !FO;); 27 | check!(f32, u8=> uidenta; qa: u8=> a: -1.0, !FU; a: 256.0, !FO;); 28 | check!(f32, u16=> uidenta; qa: u16=> a: -1.0, !FU; a: 65_536.0, !FO;); 29 | check!(f32, u32=> uidenta; qa: u32=> 30 | a: 4.294967e9, 4294967040; 31 | a: -1.0, !FU; a: 4_294_968_000.0, !FO;); 32 | check!(f32, u64=> uidenta; qa: u64=> 33 | a: 1.8446743e19, 18446742974197923840; 34 | a: -1.0, !FU; a: 18_446_746_000_000_000_000.0, !FO;); 35 | } 36 | 37 | #[test] 38 | fn test_f64_to_int() { 39 | check!(f64, i8=> sidenta; qa: i8=> a: -129.0, !FU; a: 128.0, !FO;); 40 | check!(f64, i16=> sidenta; qa: i16=> a: -32_769.0, !FU; a: 32_768.0, !FO;); 41 | check!(f64, i32=> sidenta; qa: i32=> a: -2_147_483_649.0, !FU; a: 2_147_483_648.0, !FO;); 42 | check!(f64, i64=> sidenta; qa: i64=> 43 | a: -9.223372036854776e18, -9223372036854775808; 44 | a: 9.223372036854775e18, 9223372036854774784; 45 | a: -9_223_372_036_854_778_000.0, !FU; a: 9_223_372_036_854_778_000.0, !FO;); 46 | check!(f64, u8=> uidenta; qa: u8=> a: -1.0, !FU; a: 256.0, !FO;); 47 | check!(f64, u16=> uidenta; qa: u16=> a: -1.0, !FU; a: 65_536.0, !FO;); 48 | check!(f64, u32=> uidenta; qa: u32=> a: -1.0, !FU; a: 4_294_967_296.0, !FO;); 49 | check!(f64, u64=> uidenta; qa: u64=> 50 | a: 1.844674407370955e19; 51 | a: -1.0, !FU; a: 18_446_744_073_709_560_000.0, !FO;); 52 | } 53 | 54 | #[test] 55 | fn test_f64() { 56 | check!(f64, f32=> fidenta; qa: *;); 57 | check!(f64, f64=> fident; qv: *;); 58 | } 59 | -------------------------------------------------------------------------------- /tests/lang_ints.rs: -------------------------------------------------------------------------------- 1 | extern crate conv; 2 | 3 | #[macro_use] mod util; 4 | 5 | use conv::*; 6 | 7 | use conv::NegOverflow as Uf; 8 | use conv::PosOverflow as Of; 9 | use conv::RangeError::NegOverflow as RU; 10 | use conv::RangeError::PosOverflow as RO; 11 | 12 | #[test] 13 | fn test_i8() { 14 | check!(i8, i8=> sident; qv: *; qa: *; qaW: *); 15 | check!(i8, i16=> sident; qv: *; qa: *; qaW: *); 16 | check!(i8, i32=> sident; qv: *; qa: *; qaW: *); 17 | check!(i8, i64=> sident; qv: *; qa: *; qaW: *); 18 | check!(i8, u8=> uident; qv: +; qa: +; qaW: *; 19 | v: -1, !Uf; 20 | ); 21 | check!(i8, u16=> uident; qv: +; qa: +; qaW: *; 22 | v: -1, !Uf; 23 | ); 24 | check!(i8, u32=> uident; qv: +; qa: +; qaW: *; 25 | v: -1, !Uf; 26 | ); 27 | check!(i8, u64=> uident; qv: +; qa: +; qaW: *; 28 | v: -1, !Uf; 29 | ); 30 | check!(i8, isize=> sident; qv: *; qa: *; qaW: *); 31 | check!(i8, usize=> uident; qv: +; qa: +; qaW: *; 32 | v: -1, !Uf; 33 | ); 34 | } 35 | 36 | #[test] 37 | fn test_i16() { 38 | check!(i16, i8=> sident; qv: i8=> qa: i8=> qaW: *; 39 | v: -129, !RU; v: 128, !RO; 40 | ); 41 | check!(i16, i16=> sident; qv: *; qa: *; qaW: *); 42 | check!(i16, i32=> sident; qv: *; qa: *; qaW: *); 43 | check!(i16, i64=> sident; qv: *; qa: *; qaW: *); 44 | check!(i16, u8=> uident; qv: u8=> qa: +; qaW: *; 45 | v: -1, !RU; 46 | ); 47 | check!(i16, u16=> uident; qv: u16, i16=> qa: +; qaW: *; 48 | v: -1, !Uf; 49 | ); 50 | check!(i16, u32=> uident; qv: +; qa: +; qaW: *; 51 | v: -1, !Uf; 52 | ); 53 | check!(i16, u64=> uident; qv: +; qa: +; qaW: *; 54 | v: -1, !Uf; 55 | ); 56 | check!(i16, isize=> sident; qv: *; qa: *; qaW: *); 57 | check!(i16, usize=> uident; qv: +; qa: +; qaW: *; 58 | v: -1, !Uf; 59 | ); 60 | } 61 | 62 | #[test] 63 | fn test_i32() { 64 | check!(i32, i8=> sident; qv: i8=> qa: i8=> qaW: *; 65 | v: -129, !RU; v: 128, !RO; 66 | ); 67 | check!(i32, i16=> sident; qv: i16=> qa: i16=> qaW: *; 68 | v: -32_769, !RU; v: 32_768, !RO; 69 | ); 70 | check!(i32, i32=> sident; qv: *; qa: *; qaW: *); 71 | check!(i32, i64=> sident; qv: *; qa: *; qaW: *); 72 | check!(i32, u8=> uident; qv: u8=> qa: u8=> qaW: *; 73 | v: -1, !RU; 74 | ); 75 | check!(i32, u16=> uident; qv: u16=> qa: u16=> qaW: *; 76 | v: -1, !RU; 77 | ); 78 | check!(i32, u32=> uident; qv: +; qa: +; qaW: *; 79 | v: -1, !Uf; 80 | ); 81 | check!(i32, u64=> uident; qv: +; qa: +; qaW: *; 82 | v: -1, !Uf; 83 | ); 84 | for_bitness! { 85 | 32 { 86 | check!(i32, isize=> sident; qv: *; qa: *; qaW: *); 87 | check!(i32, usize=> uident; qv: +; qa: +; qaW: *; 88 | v: -1, !Uf; 89 | ); 90 | } 91 | 64 { 92 | check!(i32, isize=> sident; qv: *; qa: *; qaW: *); 93 | check!(i32, usize=> uident; qv: +; qa: +; qaW: *; 94 | v: -1, !Uf; 95 | ); 96 | } 97 | } 98 | } 99 | 100 | #[test] 101 | fn test_i64() { 102 | check!(i64, i8=> sident; qv: i8=> qa: i8=> qaW: *; 103 | v: -129, !RU; v: 128, !RO; 104 | ); 105 | check!(i64, i16=> sident; qv: i16=> qa: i16=> qaW: *; 106 | v: -32_769, !RU; v: 32_768, !RO; 107 | ); 108 | check!(i64, i32=> sident; qv: i32=> qa: i32=> qaW: *; 109 | v: -2_147_483_649, !RU; v: 2_147_483_648, !RO; 110 | ); 111 | check!(i64, i64=> sident; qv: *; qa: *; qaW: *; 112 | ); 113 | check!(i64, u8=> uident; qv: u8=> qa: u8=> qaW: *; 114 | v: -1, !RU; 115 | ); 116 | check!(i64, u16=> uident; qv: u16=> qa: u16=> qaW: *; 117 | v: -1, !RU; 118 | ); 119 | check!(i64, u32=> uident; qv: u32=> qa: u32=> qaW: *; 120 | v: -1, !RU; 121 | ); 122 | check!(i64, u64=> uident; qv: +; qa: +; qaW: *; 123 | v: -1, !Uf; 124 | ); 125 | for_bitness! { 126 | 32 { 127 | check!(i64, isize=> sident; qv: isize=> qa: isize=> qaW: *; 128 | v: -2_147_483_649, !RU; v: 2_147_483_648, !RO; 129 | ); 130 | check!(i64, usize=> uident; qv: usize=> qa: usize=> qaW: *; 131 | v: -1, !RU; v: 4_294_967_296, !RO; 132 | ); 133 | } 134 | 64 { 135 | check!(i64, isize=> sident; qv: *; qa: *; qaW: *; 136 | ); 137 | check!(i64, usize=> uident; qv: +; qa: +; qaW: *; 138 | v: -1, !Uf; 139 | ); 140 | } 141 | } 142 | } 143 | 144 | #[test] 145 | fn test_u8() { 146 | check!(u8, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; 147 | v: 127; v: 128, !Of; 148 | ); 149 | check!(u8, i16=> uident; qv: *; qa: *; qaW: *); 150 | check!(u8, i32=> uident; qv: *; qa: *; qaW: *); 151 | check!(u8, i64=> uident; qv: *; qa: *; qaW: *); 152 | check!(u8, u8=> uident; qv: *; qa: *; qaW: *); 153 | check!(u8, u16=> uident; qv: *; qa: *; qaW: *); 154 | check!(u8, u32=> uident; qv: *; qa: *; qaW: *); 155 | check!(u8, u64=> uident; qv: *; qa: *; qaW: *); 156 | check!(u8, isize=> uident; qv: *; qa: *; qaW: *); 157 | check!(u8, usize=> uident; qv: *; qa: *; qaW: *); 158 | } 159 | 160 | #[test] 161 | fn test_u16() { 162 | check!(u16, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; 163 | v: 128, !Of; 164 | ); 165 | check!(u16, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; 166 | v: 32_768, !Of; 167 | ); 168 | check!(u16, i32=> uident; qv: *; qa: *; qaW: *); 169 | check!(u16, i64=> uident; qv: *; qa: *; qaW: *); 170 | check!(u16, u8=> uident; qv: u8=> qa: u8=> qaW: *; 171 | v: 256, !Of; 172 | ); 173 | check!(u16, u16=> uident; qv: *; qa: *; qaW: *); 174 | check!(u16, u32=> uident; qv: *; qa: *; qaW: *); 175 | check!(u16, u64=> uident; qv: *; qa: *; qaW: *); 176 | check!(u16, isize=> uident; qv: *; qa: *; qaW: *); 177 | check!(u16, usize=> uident; qv: *; qa: *; qaW: *); 178 | } 179 | 180 | #[test] 181 | fn test_u32() { 182 | check!(u32, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; 183 | v: 128, !Of; 184 | ); 185 | check!(u32, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; 186 | v: 32_768, !Of; 187 | ); 188 | check!(u32, i32=> uident; qv: +i32=> qa: +i32=> qaW: *; 189 | v: 2_147_483_648, !Of; 190 | ); 191 | check!(u32, i64=> uident; qv: *; qa: *; qaW: *); 192 | check!(u32, u8=> uident; qv: u8=> qa: u8=> qaW: *; 193 | v: 256, !Of; 194 | ); 195 | check!(u32, u16=> uident; qv: u16=> qa: u16=> qaW: *; 196 | v: 65_536, !Of; 197 | ); 198 | check!(u32, u32=> uident; qv: *; qa: *; qaW: *); 199 | check!(u32, u64=> uident; qv: *; qa: *; qaW: *); 200 | for_bitness! { 201 | 32 { 202 | check!(u32, isize=> uident; qv: +isize=> qa: +isize=> qaW: *; 203 | v: 2_147_483_647; v: 2_147_483_648, !Of; 204 | ); 205 | check!(u32, usize=> uident; qv: *; qa: *; qaW: *); 206 | } 207 | 64 { 208 | check!(u32, isize=> uident; qv: *; qa: *; qaW: *); 209 | check!(u32, usize=> uident; qv: *; qa: *; qaW: *); 210 | } 211 | } 212 | } 213 | 214 | #[test] 215 | fn test_u64() { 216 | check!(u64, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; 217 | v: 128, !Of; 218 | ); 219 | check!(u64, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; 220 | v: 32_768, !Of; 221 | ); 222 | check!(u64, i32=> uident; qv: +i32=> qa: +i32=> qaW: *; 223 | v: 2_147_483_648, !Of; 224 | ); 225 | check!(u64, i64=> uident; qv: +i64=> qa: +i64=> qaW: *; 226 | v: 9_223_372_036_854_775_808, !Of; 227 | ); 228 | check!(u64, u8=> uident; qv: u8=> qa: u8=> qaW: *; 229 | v: 256, !Of; 230 | ); 231 | check!(u64, u16=> uident; qv: u16=> qa: u16=> qaW: *; 232 | v: 65_536, !Of; 233 | ); 234 | check!(u64, u32=> uident; qv: u32=> qa: u32=> qaW: *; 235 | v: 4_294_967_296, !Of; 236 | ); 237 | check!(u64, u64=> uident; qv: *; qa: *; qaW: *); 238 | for_bitness! { 239 | 32 { 240 | check!(u64, isize=> uident; qv: +isize=> qa: +isize=> qaW: *; 241 | v: 2_147_483_648, !Of; 242 | ); 243 | check!(u64, usize=> uident; qv: usize=> qa: usize=> qaW: *; 244 | v: 4_294_967_296, !Of; 245 | ); 246 | } 247 | 64 { 248 | check!(u64, isize=> uident; qv: +i64=> qa: +i64=> qaW: *; 249 | v: 9_223_372_036_854_775_808, !Of; 250 | ); 251 | check!(u64, usize=> uident; qv: *; qa: *; qaW: *); 252 | } 253 | } 254 | } 255 | 256 | #[test] 257 | fn test_isize() { 258 | check!(isize, i8=> sident; qv: i8=> qa: i8=> qaW: *; 259 | v: -129, !RU; v: 128, !RO; 260 | ); 261 | check!(isize, i16=> sident; qv: i16=> qa: i16=> qaW: *; 262 | v: -32_769, !RU; v: 32_768, !RO; 263 | ); 264 | check!(isize, u8=> uident; qv: u8=> qa: u8=> qaW: *; 265 | v: -1, !RU; v: 256, !RO; 266 | ); 267 | check!(isize, u16=> uident; qv: u16=> qa: u16=> qaW: *; 268 | v: -1, !RU; v: 65_536, !RO; 269 | ); 270 | check!(isize, isize=> sident; qv: *; qa: *; qaW: *); 271 | for_bitness! { 272 | 32 { 273 | check!(isize, i32=> sident; qv: *; qa: *; qaW: *); 274 | check!(isize, i64=> sident; qv: *; qa: *; qaW: *); 275 | check!(isize, u32=> uident; qv: +; qa: +; qaW: *; 276 | v: -1, !Uf; 277 | ); 278 | check!(isize, u64=> uident; qv: +; qa: +; qaW: *; 279 | v: -1, !Uf; 280 | ); 281 | check!(isize, usize=> uident; qv: +; qa: +; qaW: *; 282 | v: -1, !Uf; 283 | ); 284 | } 285 | 64 { 286 | check!(isize, i32=> sident; qv: *; qa: *; qaW: *); 287 | check!(isize, i64=> sident; qv: *; qa: *; qaW: *); 288 | check!(isize, u32=> uident; qv: u32=> qa: u32=> qaW: *; 289 | v: -1, !RU; v: 4_294_967_296, !RO; 290 | ); 291 | check!(isize, u64=> uident; qv: +; qa: +; qaW: *; 292 | v: -1, !Uf; 293 | ); 294 | check!(isize, usize=> uident; qv: +; qa: +; qaW: *; 295 | v: -1, !Uf; 296 | ); 297 | } 298 | } 299 | } 300 | 301 | #[test] 302 | fn test_usize() { 303 | check!(usize, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; 304 | v: 128, !Of; 305 | ); 306 | check!(usize, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; 307 | v: 32_768, !Of; 308 | ); 309 | check!(usize, u8=> uident; qv: u8=> qa: u8=> qaW: *; 310 | v: 256, !Of; 311 | ); 312 | check!(usize, u16=> uident; qv: u16=> qa: u16=> qaW: *; 313 | v: 65_536, !Of; 314 | ); 315 | check!(usize, usize=> uident; qv: *; qa: *; qaW: *); 316 | for_bitness! { 317 | 32 { 318 | check!(usize, i32=> uident; qv: +i32=> qa: +i32=> qaW: *); 319 | check!(usize, i64=> uident; qv: *; qa: *; qaW: *); 320 | check!(usize, u32=> uident; qv: *; qa: *; qaW: *); 321 | check!(usize, u64=> uident; qv: *; qa: *; qaW: *); 322 | check!(usize, isize=> uident; qv: +isize=> qa: +isize=> qaW: *); 323 | } 324 | 64 { 325 | check!(usize, i32=> uident; qv: +i32=> qa: +i32=> qaW: *); 326 | check!(usize, i64=> uident; qv: +i64=> qa: +i64=> qaW: *); 327 | check!(usize, u32=> uident; qv: u32=> qa: u32=> qaW: *; 328 | v: 4_294_967_296, !Of; 329 | ); 330 | check!(usize, u64=> uident; qv: *; qa: *; qaW: *); 331 | check!(usize, isize=> uident; qv: +isize=> qa: +isize=> qaW: *); 332 | } 333 | } 334 | } 335 | 336 | #[cfg(feature = "std")] 337 | #[test] 338 | fn test_i_to_f() { 339 | check!(i8, f32=> sident; qv: *; qa: *); 340 | check!(i16, f32=> sident; qv: *; qa: *); 341 | check!(i32, f32=> sident; qv: (+-16_777_216); qa: *; 342 | v: -16_777_217, !RU; v: 16_777_217, !RO; 343 | ); 344 | check!(i64, f32=> sident; qv: (+-16_777_216); qa: *; 345 | v: -16_777_217, !RU; v: 16_777_217, !RO; 346 | ); 347 | check!(isize, f32=> sident; qv: (+-16_777_216); qa: *; 348 | v: -16_777_217, !RU; v: 16_777_217, !RO; 349 | ); 350 | 351 | check!(u8, f32=> uident; qv: *; qa: *); 352 | check!(u16, f32=> uident; qv: *; qa: *); 353 | check!(u32, f32=> uident; qv: (, 16_777_216); qa: *; 354 | v: 16_777_217, !Of; 355 | ); 356 | check!(u64, f32=> uident; qv: (, 16_777_216); qa: *; 357 | v: 16_777_217, !Of; 358 | ); 359 | check!(usize, f32=> uident; qv: (, 16_777_216); qa: *; 360 | v: 16_777_217, !Of; 361 | ); 362 | 363 | check!(i8, f64=> sident; qv: *; qa: *); 364 | check!(i16, f64=> sident; qv: *; qa: *); 365 | check!(i32, f64=> sident; qv: *; qa: *); 366 | check!(i64, f64=> sident; qv: (+-9_007_199_254_740_992); qa: *; 367 | v: -9_007_199_254_740_993, !RU; v: 9_007_199_254_740_993, !RO; 368 | ); 369 | for_bitness! { 370 | 32 { 371 | check!(isize, f64=> sident; qv: *; qa: *); 372 | } 373 | 64 { 374 | check!(i64, f64=> sident; qv: (+-9_007_199_254_740_992); qa: *; 375 | v: -9_007_199_254_740_993, !RU; v: 9_007_199_254_740_993, !RO; 376 | ); 377 | } 378 | } 379 | 380 | check!(u8, f64=> uident; qv: *; qa: *); 381 | check!(u16, f64=> uident; qv: *; qa: *); 382 | check!(u32, f64=> uident; qv: *; qa: *); 383 | check!(u64, f64=> uident; qv: (, 9_007_199_254_740_992); qa: *; 384 | v: 9_007_199_254_740_993, !Of; 385 | ); 386 | for_bitness! { 387 | 32 { 388 | check!(usize, f64=> uident; qv: *; qa: *); 389 | } 390 | 64 { 391 | check!(u64, f64=> uident; qv: (, 9_007_199_254_740_992); qa: *; 392 | v: 9_007_199_254_740_993, !Of; 393 | ); 394 | } 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /tests/unwraps.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "std")] 2 | extern crate conv; 3 | 4 | #[macro_use] mod util; 5 | 6 | use conv::*; 7 | 8 | macro_rules! cty { 9 | ($e:expr, $t:ty) => { 10 | { let v: $t = $e; v } 11 | }; 12 | } 13 | 14 | #[test] 15 | fn test_unwraps() { 16 | assert_eq!(cty!(0i16.value_into().unwrap(), i32), 0); 17 | assert_eq!(cty!(127i16.value_into().unwrap(), i8), 127); 18 | assert_eq!(cty!(128i16.value_into().unwrap_or_saturate(), i8), 127); 19 | assert_eq!(cty!(128i16.approx().unwrap_or_saturate(), i8), 127); 20 | assert_eq!(cty!(128i16.approx_by::().unwrap_or_saturate(), i8), -128); 21 | 22 | assert_eq!(cty!(16_777_216i32.value_into().unwrap(), f32), 16_777_216.0); 23 | assert_eq!(cty!(16_777_216i32.value_into().unwrap_or_inf(), f32), 16_777_216.0); 24 | assert_eq!(cty!(16_777_217i32.value_into().unwrap_or_inf(), f32), std::f32::INFINITY); 25 | assert_eq!(cty!((-16_777_217i32).value_into().unwrap_or_inf(), f32), std::f32::NEG_INFINITY); 26 | 27 | assert_eq!(cty!(16_777_216i32.value_into().unwrap_or_invalid(), f32), 16_777_216.0); 28 | assert!(cty!(16_777_217i32.value_into().unwrap_or_invalid(), f32).is_nan()); 29 | assert!(cty!((-16_777_217i32).value_into().unwrap_or_invalid(), f32).is_nan()); 30 | 31 | assert_eq!(cty!(0u8.value_into().unwrap_ok(), u16), 0); 32 | } 33 | -------------------------------------------------------------------------------- /tests/use_in_generics.rs: -------------------------------------------------------------------------------- 1 | //! Are conversions easily usable in generic code? 2 | extern crate conv; 3 | 4 | use conv::prelude::*; 5 | 6 | #[test] 7 | fn test_generic_unwrap() { 8 | fn do_conv(t: T) -> U 9 | where T: ValueInto { 10 | t.value_into().unwrap() 11 | } 12 | 13 | assert_eq!({let x: u8 = do_conv(42i32); x}, 42u8); 14 | } 15 | -------------------------------------------------------------------------------- /tests/util/mod.rs: -------------------------------------------------------------------------------- 1 | macro_rules! SL { 2 | ($($tts:tt)*) => { stringify!($($tts)*) }; 3 | } 4 | 5 | macro_rules! as_expr { 6 | ($e:expr) => {$e}; 7 | } 8 | 9 | macro_rules! check { 10 | (@ $from:ty, $to:ty=> $(;)*) => {}; 11 | 12 | (@ $from:ty, $to:ty=> cident; $($tail:tt)*) => { 13 | check!(@ $from, $to=> v: '\x00';); 14 | check!(@ $from, $to=> v: '\x01';); 15 | check!(@ $from, $to=> $($tail)*); 16 | }; 17 | 18 | (@ $from:ty, $to:ty=> uident; $($tail:tt)*) => { 19 | check!(@ $from, $to=> v: 0;); 20 | check!(@ $from, $to=> v: 1;); 21 | check!(@ $from, $to=> $($tail)*); 22 | }; 23 | 24 | (@ $from:ty, $to:ty=> sident; $($tail:tt)*) => { 25 | check!(@ $from, $to=> v: -1;); 26 | check!(@ $from, $to=> v: 0;); 27 | check!(@ $from, $to=> v: 1;); 28 | check!(@ $from, $to=> $($tail)*); 29 | }; 30 | 31 | (@ $from:ty, $to:ty=> fident; $($tail:tt)*) => { 32 | check!(@ $from, $to=> v: -1.0;); 33 | check!(@ $from, $to=> v: 0.0;); 34 | check!(@ $from, $to=> v: 1.0;); 35 | check!(@ $from, $to=> $($tail)*); 36 | }; 37 | 38 | (@ $from:ty, $to:ty=> uidenta; $($tail:tt)*) => { 39 | check!(@ $from, $to=> a: 0.0;); 40 | check!(@ $from, $to=> a: 1.0;); 41 | 42 | check!(@ $from, $to=> aRTN: 0.00, 0;); 43 | check!(@ $from, $to=> aRTN: 0.25, 0;); 44 | check!(@ $from, $to=> aRTN: 0.50, 1;); 45 | check!(@ $from, $to=> aRTN: 0.75, 1;); 46 | check!(@ $from, $to=> aRTN: 1.00, 1;); 47 | 48 | check!(@ $from, $to=> aRNI: 0.00, 0;); 49 | check!(@ $from, $to=> aRNI: 0.25, 0;); 50 | check!(@ $from, $to=> aRNI: 0.50, 0;); 51 | check!(@ $from, $to=> aRNI: 0.75, 0;); 52 | check!(@ $from, $to=> aRNI: 1.00, 1;); 53 | 54 | check!(@ $from, $to=> aRPI: 0.00, 0;); 55 | check!(@ $from, $to=> aRPI: 0.25, 1;); 56 | check!(@ $from, $to=> aRPI: 0.50, 1;); 57 | check!(@ $from, $to=> aRPI: 0.75, 1;); 58 | check!(@ $from, $to=> aRPI: 1.00, 1;); 59 | 60 | check!(@ $from, $to=> aRTZ: 0.00, 0;); 61 | check!(@ $from, $to=> aRTZ: 0.25, 0;); 62 | check!(@ $from, $to=> aRTZ: 0.50, 0;); 63 | check!(@ $from, $to=> aRTZ: 0.75, 0;); 64 | check!(@ $from, $to=> aRTZ: 1.00, 1;); 65 | 66 | check!(@ $from, $to=> $($tail)*); 67 | }; 68 | 69 | (@ $from:ty, $to:ty=> sidenta; $($tail:tt)*) => { 70 | check!(@ $from, $to=> a: -1.0;); 71 | check!(@ $from, $to=> a: 0.0;); 72 | check!(@ $from, $to=> a: 1.0;); 73 | 74 | check!(@ $from, $to=> aRTN: -1.00, -1;); 75 | check!(@ $from, $to=> aRTN: -0.75, -1;); 76 | check!(@ $from, $to=> aRTN: -0.50, -1;); 77 | check!(@ $from, $to=> aRTN: -0.25, 0;); 78 | check!(@ $from, $to=> aRTN: 0.00, 0;); 79 | check!(@ $from, $to=> aRTN: 0.25, 0;); 80 | check!(@ $from, $to=> aRTN: 0.50, 1;); 81 | check!(@ $from, $to=> aRTN: 0.75, 1;); 82 | check!(@ $from, $to=> aRTN: 1.00, 1;); 83 | 84 | check!(@ $from, $to=> aRNI: -1.00, -1;); 85 | check!(@ $from, $to=> aRNI: -0.75, -1;); 86 | check!(@ $from, $to=> aRNI: -0.50, -1;); 87 | check!(@ $from, $to=> aRNI: -0.25, -1;); 88 | check!(@ $from, $to=> aRNI: 0.00, 0;); 89 | check!(@ $from, $to=> aRNI: 0.25, 0;); 90 | check!(@ $from, $to=> aRNI: 0.50, 0;); 91 | check!(@ $from, $to=> aRNI: 0.75, 0;); 92 | check!(@ $from, $to=> aRNI: 1.00, 1;); 93 | 94 | check!(@ $from, $to=> aRPI: -1.00, -1;); 95 | check!(@ $from, $to=> aRPI: -0.75, 0;); 96 | check!(@ $from, $to=> aRPI: -0.50, 0;); 97 | check!(@ $from, $to=> aRPI: -0.25, 0;); 98 | check!(@ $from, $to=> aRPI: 0.00, 0;); 99 | check!(@ $from, $to=> aRPI: 0.25, 1;); 100 | check!(@ $from, $to=> aRPI: 0.50, 1;); 101 | check!(@ $from, $to=> aRPI: 0.75, 1;); 102 | check!(@ $from, $to=> aRPI: 1.00, 1;); 103 | 104 | check!(@ $from, $to=> aRTZ: -1.00, -1;); 105 | check!(@ $from, $to=> aRTZ: -0.75, 0;); 106 | check!(@ $from, $to=> aRTZ: -0.50, 0;); 107 | check!(@ $from, $to=> aRTZ: -0.25, 0;); 108 | check!(@ $from, $to=> aRTZ: 0.00, 0;); 109 | check!(@ $from, $to=> aRTZ: 0.25, 0;); 110 | check!(@ $from, $to=> aRTZ: 0.50, 0;); 111 | check!(@ $from, $to=> aRTZ: 0.75, 0;); 112 | check!(@ $from, $to=> aRTZ: 1.00, 1;); 113 | 114 | check!(@ $from, $to=> $($tail)*); 115 | }; 116 | 117 | (@ $from:ty, $to:ty=> fidenta; $($tail:tt)*) => { 118 | check!(@ $from, $to=> a: -1.0;); 119 | check!(@ $from, $to=> a: 0.0;); 120 | check!(@ $from, $to=> a: 1.0;); 121 | check!(@ $from, $to=> $($tail)*); 122 | }; 123 | 124 | (@ $from:ty, $to:ty=> v: $src:expr, !$dst:expr; $($tail:tt)*) => { 125 | { 126 | println!("? {} => {}, v: {}, !{}", SL!($from), SL!($to), SL!($src), SL!($dst)); 127 | let src: $from = $src; 128 | let dst: Result<$to, _> = src.value_into(); 129 | assert_eq!(dst, Err($dst(src))); 130 | } 131 | check!(@ $from, $to=> $($tail)*); 132 | }; 133 | 134 | (@ $from:ty, $to:ty=> v: $src:expr; $($tail:tt)*) => { 135 | { 136 | println!("? {} => {}, v: {}", SL!($from), SL!($to), SL!($src)); 137 | let src: $from = $src; 138 | let dst: Result<$to, _> = src.value_into(); 139 | assert_eq!(dst, Ok($src as $to)); 140 | } 141 | check!(@ $from, $to=> $($tail)*); 142 | }; 143 | 144 | (@ $from:ty, $to:ty=> qv: *; $($tail:tt)*) => { 145 | { 146 | extern crate quickcheck; 147 | println!("? {} => {}, qv: *", SL!($from), SL!($to)); 148 | 149 | fn property(v: $from) -> bool { 150 | let dst: Result<$to, _> = v.value_into(); 151 | dst == Ok(v as $to) 152 | } 153 | 154 | let mut qc = quickcheck::QuickCheck::new(); 155 | match qc.quicktest(property as fn($from) -> bool) { 156 | Ok(_) => (), 157 | Err(err) => panic!("qv {:?}", err) 158 | } 159 | } 160 | check!(@ $from, $to=> $($tail)*); 161 | }; 162 | 163 | (@ $from:ty, $to:ty=> qv: (+-$bound:expr); $($tail:tt)*) => { 164 | { 165 | extern crate quickcheck; 166 | println!("? {} => {}, qv: (+- {})", SL!($from), SL!($to), SL!($bound)); 167 | 168 | fn property(v: $from) -> bool { 169 | let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); 170 | if !(-$bound as $from <= v) { 171 | dst == Err(conv::FloatError::NegOverflow(v)) 172 | } else if !(v <= $bound as $from) { 173 | dst == Err(conv::FloatError::PosOverflow(v)) 174 | } else { 175 | dst == Ok(v as $to) 176 | } 177 | } 178 | 179 | let mut qc = quickcheck::QuickCheck::new(); 180 | match qc.quicktest(property as fn($from) -> bool) { 181 | Ok(_) => (), 182 | Err(err) => panic!("qv {:?}", err) 183 | } 184 | } 185 | check!(@ $from, $to=> $($tail)*); 186 | }; 187 | 188 | (@ $from:ty, $to:ty=> qv: (, $bound:expr); $($tail:tt)*) => { 189 | { 190 | extern crate quickcheck; 191 | println!("? {} => {}, qv: (, {})", SL!($from), SL!($to), SL!($bound)); 192 | 193 | fn property(v: $from) -> bool { 194 | let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); 195 | if !(v <= $bound as $from) { 196 | dst == Err(conv::FloatError::PosOverflow(v)) 197 | } else { 198 | dst == Ok(v as $to) 199 | } 200 | } 201 | 202 | let mut qc = quickcheck::QuickCheck::new(); 203 | match qc.quicktest(property as fn($from) -> bool) { 204 | Ok(_) => (), 205 | Err(err) => panic!("qv {:?}", err) 206 | } 207 | } 208 | check!(@ $from, $to=> $($tail)*); 209 | }; 210 | 211 | (@ $from:ty, $to:ty=> qv: +; $($tail:tt)*) => { 212 | { 213 | extern crate quickcheck; 214 | println!("? {} => {}, qv: +", SL!($from), SL!($to)); 215 | 216 | fn property(v: $from) -> bool { 217 | let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); 218 | if !(0 <= v) { 219 | dst == Err(conv::FloatError::NegOverflow(v)) 220 | } else { 221 | dst == Ok(v as $to) 222 | } 223 | } 224 | 225 | let mut qc = quickcheck::QuickCheck::new(); 226 | match qc.quicktest(property as fn($from) -> bool) { 227 | Ok(_) => (), 228 | Err(err) => panic!("qv {:?}", err) 229 | } 230 | } 231 | check!(@ $from, $to=> $($tail)*); 232 | }; 233 | 234 | (@ $from:ty, $to:ty=> qv: +$max:ty=> $($tail:tt)*) => { 235 | { 236 | extern crate quickcheck; 237 | println!("? {} => {}, qv: +{}", SL!($from), SL!($to), SL!($max)); 238 | 239 | fn property(v: $from) -> bool { 240 | let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); 241 | if !(v <= <$max>::max_value() as $from) { 242 | dst == Err(conv::FloatError::PosOverflow(v)) 243 | } else { 244 | dst == Ok(v as $to) 245 | } 246 | } 247 | 248 | let mut qc = quickcheck::QuickCheck::new(); 249 | match qc.quicktest(property as fn($from) -> bool) { 250 | Ok(_) => (), 251 | Err(err) => panic!("qv {:?}", err) 252 | } 253 | } 254 | check!(@ $from, $to=> $($tail)*); 255 | }; 256 | 257 | (@ $from:ty, $to:ty=> qv: $bound:ty=> $($tail:tt)*) => { 258 | { 259 | extern crate quickcheck; 260 | println!("? {} => {}, qv: {}", SL!($from), SL!($to), SL!($bound)); 261 | 262 | fn property(v: $from) -> bool { 263 | let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); 264 | if !(<$bound>::min_value() as $from <= v) { 265 | dst == Err(conv::FloatError::NegOverflow(v)) 266 | } else if !(v <= <$bound>::max_value() as $from) { 267 | dst == Err(conv::FloatError::PosOverflow(v)) 268 | } else { 269 | dst == Ok(v as $to) 270 | } 271 | } 272 | 273 | let mut qc = quickcheck::QuickCheck::new(); 274 | match qc.quicktest(property as fn($from) -> bool) { 275 | Ok(_) => (), 276 | Err(err) => panic!("qv {:?}", err) 277 | } 278 | } 279 | check!(@ $from, $to=> $($tail)*); 280 | }; 281 | 282 | (@ $from:ty, $to:ty=> qv: $min:ty, $max:ty=> $($tail:tt)*) => { 283 | { 284 | extern crate quickcheck; 285 | println!("? {} => {}, qv: {}, {}", SL!($from), SL!($to), SL!($min), SL!($max)); 286 | 287 | fn property(v: $from) -> bool { 288 | let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); 289 | if !(<$min>::min_value() as $from <= v) { 290 | dst == Err(conv::FloatError::NegOverflow(v)) 291 | } else if !(v <= <$max>::max_value() as $from) { 292 | dst == Err(conv::FloatError::PosOverflow(v)) 293 | } else { 294 | dst == Ok(v as $to) 295 | } 296 | } 297 | 298 | let mut qc = quickcheck::QuickCheck::new(); 299 | match qc.quicktest(property as fn($from) -> bool) { 300 | Ok(_) => (), 301 | Err(err) => panic!("qv {:?}", err) 302 | } 303 | } 304 | check!(@ $from, $to=> $($tail)*); 305 | }; 306 | 307 | (@ $from:ty, $to:ty=> a: $src:expr, !$dst:expr; $($tail:tt)*) => { 308 | { 309 | println!("? {} => {}, a: {}, !{}", SL!($from), SL!($to), SL!($src), SL!($dst)); 310 | let src: $from = $src; 311 | let dst: Result<$to, _> = src.approx_as(); 312 | assert_eq!(dst, Err($dst(src))); 313 | } 314 | check!(@ $from, $to=> $($tail)*); 315 | }; 316 | 317 | (@ $from:ty, $to:ty=> a: $src:expr, $dst:expr; $($tail:tt)*) => { 318 | { 319 | println!("? {} => {}, a: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); 320 | let src: $from = $src; 321 | let dst: Result<$to, _> = src.approx_as(); 322 | assert_eq!(dst, Ok($dst)); 323 | } 324 | check!(@ $from, $to=> $($tail)*); 325 | }; 326 | 327 | (@ $from:ty, $to:ty=> a: $src:expr; $($tail:tt)*) => { 328 | { 329 | println!("? {} => {}, a: {}", SL!($from), SL!($to), SL!($src)); 330 | let src: $from = $src; 331 | let dst: Result<$to, _> = src.approx_as(); 332 | assert_eq!(dst, Ok($src as $to)); 333 | } 334 | check!(@ $from, $to=> $($tail)*); 335 | }; 336 | 337 | (@ $from:ty, $to:ty=> qa: *; $($tail:tt)*) => { 338 | { 339 | extern crate quickcheck; 340 | println!("? {} => {}, qa: *", SL!($from), SL!($to)); 341 | 342 | fn property(v: $from) -> bool { 343 | let dst: Result<$to, _> = v.approx_as(); 344 | dst == Ok(v as $to) 345 | } 346 | 347 | let mut qc = quickcheck::QuickCheck::new(); 348 | match qc.quicktest(property as fn($from) -> bool) { 349 | Ok(_) => (), 350 | Err(err) => panic!("qa {:?}", err) 351 | } 352 | } 353 | check!(@ $from, $to=> $($tail)*); 354 | }; 355 | 356 | (@ $from:ty, $to:ty=> qa: +; $($tail:tt)*) => { 357 | { 358 | extern crate quickcheck; 359 | println!("? {} => {}, qa: +", SL!($from), SL!($to)); 360 | 361 | fn property(v: $from) -> bool { 362 | let dst: Result<$to, conv::FloatError<_>> = v.approx_as().map_err(From::from); 363 | if !(0 <= v) { 364 | dst == Err(conv::FloatError::NegOverflow(v)) 365 | } else { 366 | dst == Ok(v as $to) 367 | } 368 | } 369 | 370 | let mut qc = quickcheck::QuickCheck::new(); 371 | match qc.quicktest(property as fn($from) -> bool) { 372 | Ok(_) => (), 373 | Err(err) => panic!("qa {:?}", err) 374 | } 375 | } 376 | check!(@ $from, $to=> $($tail)*); 377 | }; 378 | 379 | (@ $from:ty, $to:ty=> qa: +$max:ty=> $($tail:tt)*) => { 380 | { 381 | extern crate quickcheck; 382 | println!("? {} => {}, qa: +{}", SL!($from), SL!($to), SL!($max)); 383 | 384 | fn property(v: $from) -> bool { 385 | let dst: Result<$to, conv::FloatError<_>> = v.approx_as().map_err(From::from); 386 | if !(v <= <$max>::max_value() as $from) { 387 | dst == Err(conv::FloatError::PosOverflow(v)) 388 | } else { 389 | dst == Ok(v as $to) 390 | } 391 | } 392 | 393 | let mut qc = quickcheck::QuickCheck::new(); 394 | match qc.quicktest(property as fn($from) -> bool) { 395 | Ok(_) => (), 396 | Err(err) => panic!("qa {:?}", err) 397 | } 398 | } 399 | check!(@ $from, $to=> $($tail)*); 400 | }; 401 | 402 | (@ $from:ty, $to:ty=> qa: $bound:ty=> $($tail:tt)*) => { 403 | { 404 | extern crate quickcheck; 405 | println!("? {} => {}, qa: {}", SL!($from), SL!($to), SL!($bound)); 406 | 407 | fn property(v: $from) -> bool { 408 | let dst: Result<$to, conv::FloatError<_>> = v.approx_as().map_err(From::from); 409 | if !(<$bound>::min_value() as $from <= v) { 410 | dst == Err(conv::FloatError::NegOverflow(v)) 411 | } else if !(v <= <$bound>::max_value() as $from) { 412 | dst == Err(conv::FloatError::PosOverflow(v)) 413 | } else { 414 | dst == Ok(v as $to) 415 | } 416 | } 417 | 418 | let mut qc = quickcheck::QuickCheck::new(); 419 | match qc.quicktest(property as fn($from) -> bool) { 420 | Ok(_) => (), 421 | Err(err) => panic!("qa {:?}", err) 422 | } 423 | } 424 | check!(@ $from, $to=> $($tail)*); 425 | }; 426 | 427 | (@ $from:ty, $to:ty=> qaW: *; $($tail:tt)*) => { 428 | { 429 | extern crate quickcheck; 430 | println!("? {} => {}, qaW: *", SL!($from), SL!($to)); 431 | 432 | fn property(v: $from) -> bool { 433 | let dst: Result<$to, _> = v.approx_as_by::<_, Wrapping>(); 434 | dst == Ok(v as $to) 435 | } 436 | 437 | let mut qc = quickcheck::QuickCheck::new(); 438 | match qc.quicktest(property as fn($from) -> bool) { 439 | Ok(_) => (), 440 | Err(err) => panic!("qaW {:?}", err) 441 | } 442 | } 443 | check!(@ $from, $to=> $($tail)*); 444 | }; 445 | 446 | (@ $from:ty, $to:ty=> aRTN: $src:expr, $dst:expr; $($tail:tt)*) => { 447 | { 448 | println!("? {} => {}, aRTN: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); 449 | let src: $from = $src; 450 | let dst: Result<$to, _> = src.approx_by::(); 451 | assert_eq!(dst, Ok($dst)); 452 | } 453 | check!(@ $from, $to=> $($tail)*); 454 | }; 455 | 456 | (@ $from:ty, $to:ty=> aRNI: $src:expr, $dst:expr; $($tail:tt)*) => { 457 | { 458 | println!("? {} => {}, aRNI: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); 459 | let src: $from = $src; 460 | let dst: Result<$to, _> = src.approx_by::(); 461 | assert_eq!(dst, Ok($dst)); 462 | } 463 | check!(@ $from, $to=> $($tail)*); 464 | }; 465 | 466 | (@ $from:ty, $to:ty=> aRPI: $src:expr, $dst:expr; $($tail:tt)*) => { 467 | { 468 | println!("? {} => {}, aRPI: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); 469 | let src: $from = $src; 470 | let dst: Result<$to, _> = src.approx_by::(); 471 | assert_eq!(dst, Ok($dst)); 472 | } 473 | check!(@ $from, $to=> $($tail)*); 474 | }; 475 | 476 | (@ $from:ty, $to:ty=> aRTZ: $src:expr, $dst:expr; $($tail:tt)*) => { 477 | { 478 | println!("? {} => {}, aRTZ: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); 479 | let src: $from = $src; 480 | let dst: Result<$to, _> = src.approx_by::(); 481 | assert_eq!(dst, Ok($dst)); 482 | } 483 | check!(@ $from, $to=> $($tail)*); 484 | }; 485 | 486 | ($from:ty, $to:ty=> $($tail:tt)*) => { 487 | check! { @ $from, $to=> $($tail)*; } 488 | }; 489 | } 490 | 491 | macro_rules! for_bitness { 492 | (32 {$($bits32:tt)*} 64 {$($bits64:tt)*}) => { 493 | as_expr!( 494 | { 495 | #[cfg(target_pointer_width="32")] 496 | fn for_bitness() { 497 | $($bits32)* 498 | } 499 | 500 | #[cfg(target_pointer_width="64")] 501 | fn for_bitness() { 502 | $($bits64)* 503 | } 504 | 505 | for_bitness() 506 | } 507 | ) 508 | }; 509 | } 510 | --------------------------------------------------------------------------------