conv-0.3.3/.gitignore00006440000000000000000000000022125561006340012634 0ustar0000000000000000target Cargo.lock conv-0.3.3/.travis.yml00006440000000000000000000000403126714343020012760 0ustar0000000000000000language: rust script: cargo build --verbose && cargo test --verbose rust: - 1.2.0 - 1.3.0 - 1.4.0 - 1.5.0 - 1.6.0 - stable - beta - nightly matrix: allow_failures: - rust: nightly branches: except: - /^issue-.*$/ conv-0.3.3/Cargo.toml00006440000000000000000000002064126754170750012620 0ustar0000000000000000# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "conv" version = "0.3.3" authors = ["Daniel Keep "] exclude = ["scripts/*", "update-docs.py"] description = "This crate provides a number of conversion traits with more specific semantics than those provided by 'as' or 'From'/'Into'." documentation = "https://danielkeep.github.io/rust-conv/doc/conv/index.html" readme = "README.md" keywords = ["from", "into", "conversion", "approximation"] license = "MIT" repository = "https://github.com/DanielKeep/rust-conv" [dependencies.custom_derive] version = "0.1.2" [dev-dependencies.quickcheck] version = "0.2.21, < 0.2.25" conv-0.3.3/Cargo.toml.orig00006440000000000000000000001166126754170750013561 0ustar0000000000000000[package] name = "conv" version = "0.3.3" authors = ["Daniel Keep "] description = "This crate provides a number of conversion traits with more specific semantics than those provided by 'as' or 'From'/'Into'." repository = "https://github.com/DanielKeep/rust-conv" documentation = "https://danielkeep.github.io/rust-conv/doc/conv/index.html" readme = "README.md" license = "MIT" keywords = ["from", "into", "conversion", "approximation"] exclude = [ "scripts/*", "update-docs.py", ] [dependencies] custom_derive = "0.1.2" [dev-dependencies] quickcheck = "0.2.21, < 0.2.25" conv-0.3.3/LICENSE00006440000000000000000000002070125611161150011653 0ustar0000000000000000Copyright (c) 2015 Daniel Keep Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. conv-0.3.3/README.md00006440000000000000000000014052126714343030012134 0ustar0000000000000000 # `conv` This crate provides a number of conversion traits with more specific semantics than those provided by `as` or `From`/`Into`. 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. 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. **Links** * [Latest Release](https://crates.io/crates/scan-rules/) * [Latest Docs](https://danielkeep.github.io/rust-scan-rules/doc/scan_rules/index.html) * [Repository](https://github.com/DanielKeep/rust-scan-rules) ## Compatibility `conv` is compatible with Rust 1.2 and higher. ## Examples ```rust # extern crate conv; # use conv::*; # fn main() { // This *cannot* fail, so we can use `unwrap_ok` to discard the `Result`. assert_eq!(u8::value_from(0u8).unwrap_ok(), 0u8); // This *can* fail. Specifically, it can overflow toward negative infinity. assert_eq!(u8::value_from(0i8), Ok(0u8)); assert_eq!(u8::value_from(-1i8), Err(NegOverflow(-1))); // This can overflow in *either* direction; hence the change to `RangeError`. assert_eq!(u8::value_from(-1i16), Err(RangeError::NegOverflow(-1))); assert_eq!(u8::value_from(0i16), Ok(0u8)); assert_eq!(u8::value_from(256i16), Err(RangeError::PosOverflow(256))); // We can use the extension traits to simplify this a little. assert_eq!(u8::value_from(-1i16).unwrap_or_saturate(), 0u8); assert_eq!(u8::value_from(0i16).unwrap_or_saturate(), 0u8); assert_eq!(u8::value_from(256i16).unwrap_or_saturate(), 255u8); // Obviously, all integers can be "approximated" using the default scheme (it // doesn't *do* anything), but they can *also* be approximated with the // `Wrapping` scheme. assert_eq!( >::approx_from(400u16), Err(PosOverflow(400))); assert_eq!( >::approx_from(400u16), Ok(144u8)); // This is rather inconvenient; as such, there are a number of convenience // extension methods available via `ConvUtil` and `ConvAsUtil`. assert_eq!(400u16.approx(), Err::(PosOverflow(400))); assert_eq!(400u16.approx_by::(), Ok::(144u8)); assert_eq!(400u16.approx_as::(), Err(PosOverflow(400))); assert_eq!(400u16.approx_as_by::(), Ok(144)); // Integer -> float conversions *can* fail due to limited precision. // Once the continuous range of exactly representable integers is exceeded, the // provided implementations fail with overflow errors. assert_eq!(f32::value_from(16_777_216i32), Ok(16_777_216.0f32)); assert_eq!(f32::value_from(16_777_217i32), Err(RangeError::PosOverflow(16_777_217))); // Float -> integer conversions have to be done using approximations. Although // exact conversions are *possible*, "advertising" this with an implementation // is misleading. // // Note that `DefaultApprox` for float -> integer uses whatever rounding // mode is currently active (*i.e.* whatever `as` would do). assert_eq!(41.0f32.approx(), Ok(41u8)); assert_eq!(41.3f32.approx(), Ok(41u8)); assert_eq!(41.5f32.approx(), Ok(41u8)); assert_eq!(41.8f32.approx(), Ok(41u8)); assert_eq!(42.0f32.approx(), Ok(42u8)); assert_eq!(255.0f32.approx(), Ok(255u8)); assert_eq!(256.0f32.approx(), Err::(FloatError::PosOverflow(256.0))); // Sometimes, it can be useful to saturate the conversion from float to // integer directly, then account for NaN as input separately. The `Saturate` // extension trait exists for this reason. assert_eq!((-23.0f32).approx_as::().saturate(), Ok(0)); assert_eq!(302.0f32.approx_as::().saturate(), Ok(255u8)); assert!(std::f32::NAN.approx_as::().saturate().is_err()); // If you really don't care about the specific kind of error, you can just rely // on automatic conversion to `GeneralErrorKind`. fn too_many_errors() -> Result<(), GeneralErrorKind> { assert_eq!({let r: u8 = try!(0u8.value_into()); r}, 0u8); assert_eq!({let r: u8 = try!(0i8.value_into()); r}, 0u8); assert_eq!({let r: u8 = try!(0i16.value_into()); r}, 0u8); assert_eq!({let r: u8 = try!(0.0f32.approx()); r}, 0u8); Ok(()) } # let _ = too_many_errors(); # } ``` ## Change Log ### v0.3.2 - Added integer ↔ `char` conversions. - Added missing `isize`/`usize` → `f32`/`f64` conversions. - Fixed the error type of `i64` → `usize` for 64-bit targets. ### v0.3.1 - Change to `unwrap_ok` for better codegen (thanks bluss). - Fix for Rust breaking change (code in question was dodgy anyway; thanks m4rw3r). ### v0.3.0 - Added an `Error` constraint to all `Err` associated types. This will break any user-defined conversions where the `Err` type does not implement `Error`. - 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. ### v0.2.1 - Added `ConvUtil::into_as` as a shortcut for `Into::::into`. - Added `#[inline]` attributes. - Added `Saturate::saturate`, which can saturate `Result`s arising from over/underflow. ### v0.2.0 - 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. - Added the normal rounding modes for float → int approximations: `RoundToNearest`, `RoundToNegInf`, `RoundToPosInf`, and `RoundToZero`. - `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. conv-0.3.3/src/errors.rs00006440000000000000000000042756126270632740013350 0ustar0000000000000000/*! This module defines the various error types that can be produced by a failed conversion. In addition, it also defines some extension traits to make working with failable conversions more ergonomic (see the `Unwrap*` traits). */ use std::any::Any; use std::error::Error; use std::fmt::{self, Debug, Display}; use misc::{Saturated, InvalidSentinel, SignedInfinity}; macro_rules! Desc { ( ($desc:expr) pub struct $name:ident<$t:ident> $_body:tt; ) => { impl<$t> Display for $name<$t> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, $desc) } } impl<$t> Error for $name<$t> where $t: Any { fn description(&self) -> &str { $desc } } }; } macro_rules! DummyDebug { ( () pub enum $name:ident<$t:ident> { $(#[doc=$_doc:tt] $vname:ident($_vpay:ident),)+ } ) => { impl<$t> Debug for $name<$t> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { let msg = match *self { $($name::$vname(_) => stringify!($vname),)+ }; write!(fmt, concat!(stringify!($name), "::{}(..)"), msg) } } }; ( () pub struct $name:ident<$t:ident>(pub $_pay:ident); ) => { impl<$t> Debug for $name<$t> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, concat!(stringify!($name), "(..)")) } } }; } macro_rules! EnumDesc { ( ($($vname:ident => $vdesc:expr,)+) pub enum $name:ident $_body:tt ) => { impl Display for $name { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "{}", match *self { $($name::$vname => $vdesc,)+ }) } } impl Error for $name { fn description(&self) -> &str { match *self { $($name::$vname => $vdesc,)+ } } } }; ( ($($vname:ident => $vdesc:expr,)+) pub enum $name:ident<$t:ident> $_body:tt ) => { impl<$t> Display for $name<$t> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "{}", match *self { $($name::$vname(..) => $vdesc,)+ }) } } impl<$t> Error for $name<$t> where $t: Any { fn description(&self) -> &str { match *self { $($name::$vname(..) => $vdesc,)+ } } } }; } macro_rules! FromName { ( ($fname:ident) pub enum $name:ident<$t:ident> $_body:tt ) => { impl<$t> From<$fname<$t>> for $name<$t> { #[inline] fn from(e: $fname<$t>) -> Self { $name::$fname(e.into_inner()) } } }; ( ($fname:ident<$t:ident>) pub enum $name:ident $_body:tt ) => { impl<$t> From<$fname<$t>> for $name { #[inline] fn from(_: $fname<$t>) -> Self { $name::$fname } } }; } macro_rules! FromNoError { ( () pub enum $name:ident $_body:tt ) => { impl From for $name { #[inline] fn from(_: NoError) -> Self { panic!(concat!("cannot convert NoError into ", stringify!($name))) } } }; ( () pub enum $name:ident<$t:ident> $_body:tt ) => { impl<$t> From for $name<$t> { fn from(_: NoError) -> Self { panic!(concat!("cannot convert NoError into ", stringify!($name))) } } }; ( () pub struct $name:ident<$t:ident> $_body:tt; ) => { impl<$t> From for $name<$t> { fn from(_: NoError) -> Self { panic!(concat!("cannot convert NoError into ", stringify!($name))) } } }; } macro_rules! FromRemap { ( ($from:ident($($vname:ident),+)) pub enum $name:ident $_body:tt ) => { impl From<$from> for $name { #[inline] fn from(e: $from) -> Self { match e { $($from::$vname => $name::$vname,)+ } } } }; ( ($from:ident<$t:ident>($($vname:ident),+)) pub enum $name:ident $_body:tt ) => { impl<$t> From<$from<$t>> for $name { #[inline] fn from(e: $from<$t>) -> Self { match e { $($from::$vname(..) => $name::$vname,)+ } } } }; ( ($from:ident($($vname:ident),+)) pub enum $name:ident<$t:ident> $_body:tt ) => { impl<$t> From<$from<$t>> for $name<$t> { #[inline] fn from(e: $from<$t>) -> Self { match e { $($from::$vname(v) => $name::$vname(v),)+ } } } }; } macro_rules! IntoInner { ( () pub enum $name:ident<$t:ident> { $(#[doc=$_doc:tt] $vname:ident($_vpay:ident),)+ } ) => { impl<$t> $name<$t> { /// Returns the value stored in this error. #[inline] pub fn into_inner(self) -> $t { match self { $($name::$vname(v))|+ => v } } } }; ( () pub struct $name:ident<$t:ident>(pub $_pay:ident); ) => { impl<$t> $name<$t> { /// Returns the value stored in this error. #[inline] pub fn into_inner(self) -> $t { self.0 } } }; } custom_derive!{ /** A general error enumeration that subsumes all other conversion errors. This exists primarily as a "catch-all" for reliably unifying various different kinds of conversion errors. */ #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, IntoInner, DummyDebug, FromNoError, EnumDesc( NegOverflow => "conversion resulted in negative overflow", PosOverflow => "conversion resulted in positive overflow", Unrepresentable => "could not convert unrepresentable value", ), FromName(Unrepresentable), FromName(NegOverflow), FromName(PosOverflow), FromRemap(RangeError(NegOverflow, PosOverflow)) )] pub enum GeneralError { /// Input was too negative for the target type. NegOverflow(T), /// Input was too positive for the target type. PosOverflow(T), /// Input was not representable in the target type. Unrepresentable(T), } } impl From> for GeneralError { #[inline] fn from(e: FloatError) -> GeneralError { use self::FloatError as F; use self::GeneralError as G; match e { F::NegOverflow(v) => G::NegOverflow(v), F::PosOverflow(v) => G::PosOverflow(v), F::NotANumber(v) => G::Unrepresentable(v), } } } custom_derive! { /** A general error enumeration that subsumes all other conversion errors, but discards all input payloads the errors may be carrying. This exists primarily as a "catch-all" for reliably unifying various different kinds of conversion errors, and between different input types. */ #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, FromNoError, EnumDesc( NegOverflow => "conversion resulted in negative overflow", PosOverflow => "conversion resulted in positive overflow", Unrepresentable => "could not convert unrepresentable value", ), FromName(Unrepresentable), FromName(NegOverflow), FromName(PosOverflow), FromRemap(RangeErrorKind(NegOverflow, PosOverflow)), FromRemap(RangeError(NegOverflow, PosOverflow)), FromRemap(GeneralError(NegOverflow, PosOverflow, Unrepresentable)) )] pub enum GeneralErrorKind { /// Input was too negative for the target type. NegOverflow, /// Input was too positive for the target type. PosOverflow, /// Input was not representable in the target type. Unrepresentable, } } impl From> for GeneralErrorKind { #[inline] fn from(e: FloatError) -> GeneralErrorKind { use self::FloatError as F; use self::GeneralErrorKind as G; match e { F::NegOverflow(..) => G::NegOverflow, F::PosOverflow(..) => G::PosOverflow, F::NotANumber(..) => G::Unrepresentable, } } } /** Indicates that it is not possible for the conversion to fail. 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"). */ #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] pub enum NoError {} impl Display for NoError { fn fmt(&self, _: &mut fmt::Formatter) -> Result<(), fmt::Error> { unreachable!() } } impl Error for NoError { fn description(&self) -> &str { unreachable!() } } custom_derive! { /// Indicates that the conversion failed because the value was not representable. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, IntoInner, DummyDebug, FromNoError, Desc("could not convert unrepresentable value") )] pub struct Unrepresentable(pub T); } custom_derive! { /// Indicates that the conversion failed due to a negative overflow. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, IntoInner, DummyDebug, FromNoError, Desc("conversion resulted in negative overflow") )] pub struct NegOverflow(pub T); } custom_derive! { /// Indicates that the conversion failed due to a positive overflow. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, IntoInner, DummyDebug, FromNoError, Desc("conversion resulted in positive overflow") )] pub struct PosOverflow(pub T); } custom_derive! { /** Indicates that a conversion from a floating point type failed. */ #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, IntoInner, DummyDebug, FromNoError, EnumDesc( NegOverflow => "conversion resulted in negative overflow", PosOverflow => "conversion resulted in positive overflow", NotANumber => "conversion target does not support not-a-number", ), FromName(NegOverflow), FromName(PosOverflow), FromRemap(RangeError(NegOverflow, PosOverflow)) )] pub enum FloatError { /// Input was too negative for the target type. NegOverflow(T), /// Input was too positive for the target type. PosOverflow(T), /// Input was not-a-number, which the target type could not represent. NotANumber(T), } } custom_derive! { /** Indicates that a conversion failed due to a range error. */ #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, IntoInner, DummyDebug, FromNoError, EnumDesc( NegOverflow => "conversion resulted in negative overflow", PosOverflow => "conversion resulted in positive overflow", ), FromName(NegOverflow), FromName(PosOverflow) )] pub enum RangeError { /// Input was too negative for the target type. NegOverflow(T), /// Input was too positive the target type. PosOverflow(T), } } custom_derive! { /** Indicates that a conversion failed due to a range error. 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. */ #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, FromNoError, EnumDesc( NegOverflow => "conversion resulted in negative overflow", PosOverflow => "conversion resulted in positive overflow", ), FromName(NegOverflow), FromName(PosOverflow), FromRemap(RangeError(NegOverflow, PosOverflow)) )] pub enum RangeErrorKind { /// Input was too negative for the target type. NegOverflow, /// Input was too positive for the target type. PosOverflow, } } /** Saturates a `Result`. */ pub trait Saturate { /// The result of saturating. type Output; /** Replaces an overflow error with a saturated value. 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". 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. */ fn saturate(self) -> Self::Output; } impl Saturate for Result> where T: Saturated { type Output = Result>; #[inline] fn saturate(self) -> Self::Output { use self::FloatError::*; match self { Ok(v) => Ok(v), Err(NegOverflow(_)) => Ok(T::saturated_min()), Err(PosOverflow(_)) => Ok(T::saturated_max()), Err(NotANumber(v)) => Err(Unrepresentable(v)) } } } impl Saturate for Result> where T: Saturated { type Output = Result; #[inline] fn saturate(self) -> Self::Output { use self::RangeError::*; match self { Ok(v) => Ok(v), Err(NegOverflow(_)) => Ok(T::saturated_min()), Err(PosOverflow(_)) => Ok(T::saturated_max()) } } } impl Saturate for Result where T: Saturated { type Output = Result; #[inline] fn saturate(self) -> Self::Output { use self::RangeErrorKind::*; match self { Ok(v) => Ok(v), Err(NegOverflow) => Ok(T::saturated_min()), Err(PosOverflow) => Ok(T::saturated_max()) } } } /** Safely unwrap a `Result` that cannot contain an error. */ pub trait UnwrapOk { /** Unwraps a `Result` without possibility of failing. Technically, this is not necessary; it's provided simply to make user code a little clearer. */ fn unwrap_ok(self) -> T; } impl UnwrapOk for Result { #[inline] fn unwrap_ok(self) -> T { match self { Ok(v) => v, Err(no_error) => match no_error {}, } } } /** Unwrap a conversion by saturating to infinity. */ pub trait UnwrapOrInf { /// The result of unwrapping. type Output; /** Either unwraps the successfully converted value, or saturates to infinity in the "direction" of overflow. */ fn unwrap_or_inf(self) -> Self::Output; } /** Unwrap a conversion by replacing a failure with an invalid sentinel value. */ pub trait UnwrapOrInvalid { /// The result of unwrapping. type Output; /** Either unwraps the successfully converted value, or returns the output type's invalid sentinel value. */ fn unwrap_or_invalid(self) -> Self::Output; } /** Unwrap a conversion by saturating. */ pub trait UnwrapOrSaturate { /// The result of unwrapping. type Output; /** Either unwraps the successfully converted value, or saturates in the "direction" of overflow. */ fn unwrap_or_saturate(self) -> Self::Output; } impl UnwrapOrInf for Result where T: SignedInfinity, E: Into { type Output = T; #[inline] fn unwrap_or_inf(self) -> T { use self::RangeErrorKind::*; match self.map_err(Into::into) { Ok(v) => v, Err(NegOverflow) => T::neg_infinity(), Err(PosOverflow) => T::pos_infinity(), } } } impl UnwrapOrInvalid for Result where T: InvalidSentinel { type Output = T; #[inline] fn unwrap_or_invalid(self) -> T { match self { Ok(v) => v, Err(..) => T::invalid_sentinel(), } } } impl UnwrapOrSaturate for Result where T: Saturated, E: Into { type Output = T; #[inline] fn unwrap_or_saturate(self) -> T { use self::RangeErrorKind::*; match self.map_err(Into::into) { Ok(v) => v, Err(NegOverflow) => T::saturated_min(), Err(PosOverflow) => T::saturated_max(), } } } conv-0.3.3/src/impls.rs00006440000000000000000000053050126754167140013151 0ustar0000000000000000macro_rules! max_of { ($name:ident) => { ::std::$name::MAX }; } macro_rules! min_of { ($name:ident) => { ::std::$name::MIN }; } macro_rules! approx_blind { (($($attrs:tt)*), $src:ty, $dst:ty, $scheme:ty) => { as_item! { $($attrs)* impl ::ApproxFrom<$src, $scheme> for $dst { type Err = ::errors::NoError; #[inline] fn approx_from(src: $src) -> Result<$dst, Self::Err> { Ok(src as $dst) } } } }; } macro_rules! approx_z_to_dmax { (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => { as_item! { $($attrs)* impl ::ApproxFrom<$src, $scheme> for $dst { type Err = ::errors::RangeError<$src>; #[inline] fn approx_from(src: $src) -> Result<$dst, Self::Err> { if !(0 <= src) { return Err(::errors::RangeError::NegOverflow(src)); } if !(src <= max_of!($dst) as $src) { return Err(::errors::RangeError::PosOverflow(src)); } Ok(src as $dst) } } } }; } macro_rules! approx_to_dmax { (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => { as_item! { $($attrs)* impl ::ApproxFrom<$src, $scheme> for $dst { type Err = ::errors::PosOverflow<$src>; #[inline] fn approx_from(src: $src) -> Result<$dst, Self::Err> { if !(src <= max_of!($dst) as $src) { return Err(::errors::PosOverflow(src)); } Ok(src as $dst) } } } }; } macro_rules! approx_dmin_to_dmax { (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => { as_item! { $($attrs)* impl ::ApproxFrom<$src, $scheme> for $dst { type Err = ::errors::RangeError<$src>; #[inline] fn approx_from(src: $src) -> Result<$dst, Self::Err> { if !(min_of!($dst) as $src <= src) { return Err(::errors::RangeError::NegOverflow(src)); } if !(src <= max_of!($dst) as $src) { return Err(::errors::RangeError::PosOverflow(src)); } Ok(src as $dst) } } } } } macro_rules! approx_z_up { (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => { as_item! { $($attrs)* impl ::ApproxFrom<$src, $scheme> for $dst { type Err = ::errors::NegOverflow<$src>; #[inline] fn approx_from(src: $src) -> Result<$dst, Self::Err> { if !(0 <= src) { return Err(::errors::NegOverflow(src)); } Ok(src as $dst) } } } }; } macro_rules! approx_dmin_to_dmax_no_nan { (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty) => { approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, $scheme, approx: |s| s } }; (($($attrs:tt)*), $src:ty, $dst:ident, $scheme:ty, approx: |$src_name:ident| $conv:expr) => { approx_range_no_nan! { ($($attrs)*), $src, $dst, [min_of!($dst) as $src, max_of!($dst) as $src], $scheme, approx: |$src_name| $conv } }; } macro_rules! approx_range_no_nan { (($($attrs:tt)*), $src:ty, $dst:ident, [$min:expr, $max:expr], $scheme:ty) => { approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max], $scheme, approx: |s| s } }; (($($attrs:tt)*), $src:ty, $dst:ident, [$min:expr, $max:expr], $scheme:ty, approx: |$src_name:ident| $conv:expr) => { as_item! { $($attrs)* impl ::ApproxFrom<$src, $scheme> for $dst { type Err = ::errors::FloatError<$src>; #[inline] fn approx_from(src: $src) -> Result<$dst, Self::Err> { if src.is_nan() { return Err(::errors::FloatError::NotANumber(src)); } let approx = { let $src_name = src; $conv }; if !($min <= approx) { return Err(::errors::FloatError::NegOverflow(src)); } if !(approx <= $max) { return Err(::errors::FloatError::PosOverflow(src)); } Ok(approx as $dst) } } } }; } macro_rules! num_conv { (@ $src:ty=> $(,)*) => {}; (@ $src:ty=> #[32] $($tail:tt)*) => { num_conv! { @ $src=> (#[cfg(target_pointer_width="32")]) $($tail)* } }; (@ $src:ty=> #[64] $($tail:tt)*) => { num_conv! { @ $src=> (#[cfg(target_pointer_width="64")]) $($tail)* } }; (@ $src:ty=> e $($tail:tt)*) => { num_conv! { @ $src=> () e $($tail)* } }; (@ $src:ty=> n+ $($tail:tt)*) => { num_conv! { @ $src=> () n+ $($tail)* } }; (@ $src:ty=> n $($tail:tt)*) => { num_conv! { @ $src=> () n $($tail)* } }; (@ $src:ty=> w+ $($tail:tt)*) => { num_conv! { @ $src=> () w+ $($tail)* } }; (@ $src:ty=> w $($tail:tt)*) => { num_conv! { @ $src=> () w $($tail)* } }; (@ $src:ty=> aW $($tail:tt)*) => { num_conv! { @ $src=> () aW $($tail)* } }; (@ $src:ty=> nf $($tail:tt)*) => { num_conv! { @ $src=> () nf $($tail)* } }; (@ $src:ty=> fan $($tail:tt)*) => { num_conv! { @ $src=> () fan $($tail)* } }; // Exact conversion (@ $src:ty=> ($($attrs:tt)*) e $dst:ty, $($tail:tt)*) => { as_item! { approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::NoError; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Narrowing a signed type *into* an unsigned type where the destination type's maximum value is representable by the source type. (@ $src:ty=> ($($attrs:tt)*) n+ $dst:ident, $($tail:tt)*) => { as_item! { approx_z_to_dmax! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::RangeError<$src>; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { if !(0 <= src) { return Err(::errors::RangeError::NegOverflow(src)); } if !(src <= max_of!($dst) as $src) { return Err(::errors::RangeError::PosOverflow(src)); } Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Narrowing an unsigned type *into* a type where the destination type's maximum value is representable by the source type. (@ $src:ty=> ($($attrs:tt)*) n- $dst:ident, $($tail:tt)*) => { as_item! { approx_to_dmax! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::PosOverflow<$src>; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { if !(src <= max_of!($dst) as $src) { return Err(::errors::PosOverflow(src)); } Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Narrowing where the destination type's bounds are representable by the source type. (@ $src:ty=> ($($attrs:tt)*) n $dst:ident, $($tail:tt)*) => { as_item! { approx_dmin_to_dmax! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::RangeError<$src>; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { if !(min_of!($dst) as $src <= src) { return Err(::errors::RangeError::NegOverflow(src)); } if !(src <= max_of!($dst) as $src) { return Err(::errors::RangeError::PosOverflow(src)); } Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Widening a signed type *into* an unsigned type. (@ $src:ty=> ($($attrs:tt)*) w+ $dst:ident, $($tail:tt)*) => { as_item! { approx_z_up! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::NegOverflow<$src>; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { if !(0 <= src) { return Err(::errors::NegOverflow(src)); } Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Widening. (@ $src:ty=> ($($attrs:tt)*) w $dst:ident, $($tail:tt)*) => { as_item! { approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_blind! { ($($attrs)*), $src, $dst, ::Wrapping } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::NoError; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Narrowing *into* a floating-point type where the conversion is only exact within a given range. (@ $src:ty=> ($($attrs:tt)*) nf [+- $bound:expr] $dst:ident, $($tail:tt)*) => { as_item! { approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::RangeError<$src>; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { if !(-$bound <= src) { return Err(::errors::RangeError::NegOverflow(src)); } if !(src <= $bound) { return Err(::errors::RangeError::PosOverflow(src)); } Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; (@ $src:ty=> ($($attrs:tt)*) nf [, $max:expr] $dst:ident, $($tail:tt)*) => { as_item! { approx_blind! { ($($attrs)*), $src, $dst, ::DefaultApprox } $($attrs)* impl ::ValueFrom<$src> for $dst { type Err = ::errors::PosOverflow<$src>; #[inline] fn value_from(src: $src) -> Result<$dst, Self::Err> { if !(src <= $max) { return Err(::errors::PosOverflow(src)); } Ok(src as $dst) } } } num_conv! { @ $src=> $($tail)* } }; // Approximately narrowing a floating point value *into* a type where the source value is constrained by the given range of values. (@ $src:ty=> ($($attrs:tt)*) fan [$min:expr, $max:expr] $dst:ident, $($tail:tt)*) => { as_item! { approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max], ::DefaultApprox } approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max], ::RoundToNearest, approx: |s| s.round() } approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max], ::RoundToNegInf, approx: |s| s.floor() } approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max], ::RoundToPosInf, approx: |s| s.ceil() } approx_range_no_nan! { ($($attrs)*), $src, $dst, [$min, $max], ::RoundToZero, approx: |s| s.trunc() } } num_conv! { @ $src=> $($tail)* } }; (@ $src:ty=> ($($attrs:tt)*) fan $dst:ident, $($tail:tt)*) => { as_item! { approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::DefaultApprox } approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToNearest, approx: |s| s.round() } approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToNegInf, approx: |s| s.floor() } approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToPosInf, approx: |s| s.ceil() } approx_dmin_to_dmax_no_nan! { ($($attrs)*), $src, $dst, ::RoundToZero, approx: |s| s.trunc() } } num_conv! { @ $src=> $($tail)* } }; ($src:ty=> $($tail:tt)*) => { num_conv! { @ $src=> $($tail)*, } }; } mod lang_ints { num_conv! { i8=> w i16, w i32, w i64, w+u8, w+u16, w+u32, w+u64, w isize, w+usize } num_conv! { i16=> n i8, w i32, w i64, n+u8, w+u16, w+u32, w+u64, w isize, w+usize } num_conv! { i32=> n i8, n i16, w i64, n+u8, n+u16, w+u32, w+u64 } num_conv! { i64=> n i8, n i16, n i32, n+u8, n+u16, n+u32, w+u64 } num_conv! { i32=> #[32] e isize, #[64] w isize, w+usize } num_conv! { i64=> #[32] n isize, #[64] e isize, #[32] n+usize, #[64] w+usize } num_conv! { u8=> n-i8, w i16, w i32, w i64, w u16, w u32, w u64, w isize, w usize } num_conv! { u16=> n-i8, n-i16, w i32, w i64, n-u8, w u32, w u64, w isize, w usize } num_conv! { u32=> n-i8, n-i16, n-i32, w i64, n-u8, n-u16, w u64 } num_conv! { u64=> n-i8, n-i16, n-i32, n-i64, n-u8, n-u16, n-u32 } num_conv! { u32=> #[32] n-isize, #[64] w isize, #[32] e usize, #[64] w usize } num_conv! { u64=> n-isize, #[32] n-usize, #[64] e usize } num_conv! { isize=> n i8, n i16, #[32] e i32, #[32] w i64, #[64] n i32, #[64] e i64 } num_conv! { isize=> n+u8, n+u16, #[32] w+u32, #[32] w+u64, #[64] n+u32, #[64] w+u64 } num_conv! { isize=> w+usize } num_conv! { usize=> n-i8, n-i16, #[32] n-i32, #[32] w i64, #[64] n-i32, #[64] n-i64 } num_conv! { usize=> n-u8, n-u16, #[32] e u32, #[32] w u64, #[64] n-u32, #[64] e u64 } num_conv! { usize=> n-isize } } mod lang_floats { use {ApproxFrom, ApproxScheme}; use ValueFrom; use errors::{NoError, RangeError}; // f32 -> f64: strictly widening impl ApproxFrom for f64 where Scheme: ApproxScheme { type Err = NoError; #[inline] fn approx_from(src: f32) -> Result { Ok(src as f64) } } impl ValueFrom for f64 { type Err = NoError; #[inline] fn value_from(src: f32) -> Result { Ok(src as f64) } } // f64 -> f32: narrowing, approximate impl ApproxFrom for f32 { type Err = RangeError; #[inline] fn approx_from(src: f64) -> Result { if !src.is_finite() { return Ok(src as f32); } if !(::std::f32::MIN as f64 <= src) { return Err(RangeError::NegOverflow(src)); } if !(src <= ::std::f32::MAX as f64) { return Err(RangeError::PosOverflow(src)); } Ok(src as f32) } } } mod lang_int_to_float { num_conv! { i8=> w f32, w f64 } num_conv! { i16=> w f32, w f64 } num_conv! { i32=> nf [+- 16_777_216] f32, w f64 } num_conv! { i64=> nf [+- 16_777_216] f32, nf [+- 9_007_199_254_740_992] f64 } num_conv! { u8=> w f32, w f64 } num_conv! { u16=> w f32, w f64 } num_conv! { u32=> nf [, 16_777_216] f32, w f64 } num_conv! { u64=> nf [, 16_777_216] f32, nf [, 9_007_199_254_740_992] f64 } num_conv! { isize=> nf [+- 16_777_216] f32, #[32] w f64, #[64] nf [+- 9_007_199_254_740_992] f64 } num_conv! { usize=> nf [, 16_777_216] f32, #[32] w f64, #[64] nf [, 9_007_199_254_740_992] f64 } } mod lang_float_to_int { /* 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. *Yeah.* That's floating point for you! */ num_conv! { f32=> fan i8, fan i16, fan [-2.1474836e9, 2.1474835e9] i32, fan [-9.223372e18, 9.2233715e18] i64 } num_conv! { f32=> fan u8, fan u16, fan [0.0, 4.294967e9] u32, fan [0.0, 1.8446743e19] u64 } num_conv! { f32=> #[32] fan [-2.1474836e9, 2.1474835e9] isize, #[32] fan [0.0, 4.294967e9] usize, #[64] fan [-9.223372e18, 9.2233715e18] isize, #[64] fan [0.0, 1.8446743e19] usize } num_conv! { f64=> fan i8, fan i16, fan i32, fan [-9.223372036854776e18, 9.223372036854775e18] i64 } num_conv! { f64=> fan u8, fan u16, fan u32, fan [0.0, 1.844674407370955e19] u64 } num_conv! { f64=> #[32] fan isize, #[32] fan usize, #[64] fan [-9.223372036854776e18, 9.223372036854775e18] isize, #[64] fan [0.0, 1.844674407370955e19] usize } } mod lang_char_to_int { use TryFrom; use ValueFrom; use errors::{NoError, PosOverflow}; impl TryFrom for u32 { type Err = NoError; #[inline] fn try_from(src: char) -> Result { Ok(src as u32) } } impl TryFrom for usize { type Err = NoError; #[inline] fn try_from(src: char) -> Result { Ok(src as usize) } } impl TryFrom for isize { type Err = NoError; #[inline] fn try_from(src: char) -> Result { Ok(src as isize) } } macro_rules! conv_char_to_int { ($($ts:ty),* $(,)*) => { $( impl TryFrom for $ts { type Err = PosOverflow; #[inline] fn try_from(src: char) -> Result<$ts, Self::Err> { <$ts as ValueFrom<_>>::value_from(src as u32) .map_err(|_| PosOverflow(src)) } } )* }; } macro_rules! conv_char_to_int_wide { ($($ts:ty),* $(,)*) => { $( impl TryFrom for $ts { type Err = NoError; #[inline] fn try_from(src: char) -> Result<$ts, Self::Err> { <$ts as ValueFrom<_>>::value_from(src as u32) } } )* }; } conv_char_to_int! { i8, i16, i32, u8, u16 } conv_char_to_int_wide! { i64, u64 } } mod lang_int_to_char { use TryFrom; use ValueFrom; use errors::{NoError, Unrepresentable, UnwrapOk}; impl TryFrom for char { type Err = NoError; #[inline] fn try_from(src: u8) -> Result { Ok(src as char) } } impl TryFrom for char { type Err = Unrepresentable; #[inline] fn try_from(src: u16) -> Result { TryFrom::try_from( >::value_from(src).unwrap_ok() ).map_err(|_| Unrepresentable(src)) } } impl TryFrom for char { type Err = Unrepresentable; #[inline] fn try_from(src: u32) -> Result { ::std::char::from_u32(src).ok_or_else(|| Unrepresentable(src)) } } macro_rules! conv_int_to_char { ($($ts:ty),* $(,)*) => { $( impl TryFrom<$ts> for char { type Err = Unrepresentable<$ts>; #[inline] fn try_from(src: $ts) -> Result { >::value_from(src) .map_err(|_| Unrepresentable(src)) .and_then(|usv| TryFrom::try_from(usv) .map_err(|_| Unrepresentable(src))) } } )* }; } conv_int_to_char! { i8, i16, i32, i64, isize, u64, usize } } conv-0.3.3/src/lib.rs00006440000000000000000000057210126714343030012563 0ustar0000000000000000/*! This crate provides a number of conversion traits with more specific semantics than those provided by `as` or `From`/`Into`. 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. 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. ## Compatibility `conv` is compatible with Rust 1.2 and higher. ## Change Log ### v0.3.2 - Added integer ↔ `char` conversions. - Added missing `isize`/`usize` → `f32`/`f64` conversions. - Fixed the error type of `i64` → `usize` for 64-bit targets. ### v0.3.1 - Change to `unwrap_ok` for better codegen (thanks bluss). - Fix for Rust breaking change (code in question was dodgy anyway; thanks m4rw3r). ### v0.3.0 - Added an `Error` constraint to all `Err` associated types. This will break any user-defined conversions where the `Err` type does not implement `Error`. - 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. ### v0.2.1 - Added `ConvUtil::into_as` as a shortcut for `Into::::into`. - Added `#[inline]` attributes. - Added `Saturate::saturate`, which can saturate `Result`s arising from over/underflow. ### v0.2.0 - 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. - Added the normal rounding modes for float → int approximations: `RoundToNearest`, `RoundToNegInf`, `RoundToPosInf`, and `RoundToZero`. - `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. # Overview The following traits are used to define various conversion semantics: - [`ApproxFrom`](./trait.ApproxFrom.html)/[`ApproxInto`](./trait.ApproxInto.html) - approximate conversions, with selectable approximation scheme (see [`ApproxScheme`](./trait.ApproxScheme.html)). - [`TryFrom`](./trait.TryFrom.html)/[`TryInto`](./trait.TryInto.html) - general, potentially failing value conversions. - [`ValueFrom`](./trait.ValueFrom.html)/[`ValueInto`](./trait.ValueInto.html) - exact, value-preserving conversions. 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. These extension methods are provided to help with some common cases: - [`ConvUtil::approx_as`](./trait.ConvUtil.html#method.approx_as) - approximates to `Dst` with the `DefaultApprox` scheme. - [`ConvUtil::approx_as_by`](./trait.ConvUtil.html#method.approx_as_by) - approximates to `Dst` with the scheme `S`. - [`ConvUtil::into_as`](./trait.ConvUtil.html#method.into_as) - converts to `Dst` using `Into::into`. - [`ConvUtil::try_as`](./trait.ConvUtil.html#method.try_as) - converts to `Dst` using `TryInto::try_into`. - [`ConvUtil::value_as`](./trait.ConvUtil.html#method.value_as) - converts to `Dst` using `ValueInto::value_into`. - [`ConvAsUtil::approx`](./trait.ConvAsUtil.html#method.approx) - approximates to an inferred destination type with the `DefaultApprox` scheme. - [`ConvAsUtil::approx_by`](./trait.ConvAsUtil.html#method.approx_by) - approximates to an inferred destination type with the scheme `S`. - [`Saturate::saturate`](./errors/trait.Saturate.html#tymethod.saturate) - saturates on overflow. - [`UnwrapOk::unwrap_ok`](./errors/trait.UnwrapOk.html#tymethod.unwrap_ok) - unwraps results from conversions that cannot fail. - [`UnwrapOrInf::unwrap_or_inf`](./errors/trait.UnwrapOrInf.html#tymethod.unwrap_or_inf) - saturates to ±∞ on failure. - [`UnwrapOrInvalid::unwrap_or_invalid`](./errors/trait.UnwrapOrInvalid.html#tymethod.unwrap_or_invalid) - substitutes the target type's "invalid" sentinel value on failure. - [`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. A macro is provided to assist in implementing conversions: - [`TryFrom!`](./macros/index.html#tryfrom!) - derives an implementation of [`TryFrom`](./trait.TryFrom.html). If you are implementing your own types, you may also be interested in the traits contained in the [`misc`](./misc/index.html) module. ## Provided Implementations The crate provides several blanket implementations: - `*From for A` (all types can be converted from and into themselves). - `*Into for Src where Dst: *From` (`*From` implementations imply a matching `*Into` implementation). 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. ## Errors 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: - `ValueFrom for u16` cannot possibly fail, and as such it uses `NoError`. - `ValueFrom for u16` can *only* fail with a negative overflow, thus it uses the `NegOverflow` type. - `ValueFrom for u16` can overflow in either direction, hence it uses `RangeError`. - Finally, `ApproxFrom for u16` can overflow (positive or negative), or attempt to convert NaN; `FloatError` covers those three cases. 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`). 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. To help alleviate this, there is also `GeneralErrorKind`, which is simply `GeneralError` without the payload, and all errors can be converted into it directly. 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?). # Examples ``` # extern crate conv; # use conv::*; # fn main() { // This *cannot* fail, so we can use `unwrap_ok` to discard the `Result`. assert_eq!(u8::value_from(0u8).unwrap_ok(), 0u8); // This *can* fail. Specifically, it can overflow toward negative infinity. assert_eq!(u8::value_from(0i8), Ok(0u8)); assert_eq!(u8::value_from(-1i8), Err(NegOverflow(-1))); // This can overflow in *either* direction; hence the change to `RangeError`. assert_eq!(u8::value_from(-1i16), Err(RangeError::NegOverflow(-1))); assert_eq!(u8::value_from(0i16), Ok(0u8)); assert_eq!(u8::value_from(256i16), Err(RangeError::PosOverflow(256))); // We can use the extension traits to simplify this a little. assert_eq!(u8::value_from(-1i16).unwrap_or_saturate(), 0u8); assert_eq!(u8::value_from(0i16).unwrap_or_saturate(), 0u8); assert_eq!(u8::value_from(256i16).unwrap_or_saturate(), 255u8); // Obviously, all integers can be "approximated" using the default scheme (it // doesn't *do* anything), but they can *also* be approximated with the // `Wrapping` scheme. assert_eq!( >::approx_from(400u16), Err(PosOverflow(400))); assert_eq!( >::approx_from(400u16), Ok(144u8)); // This is rather inconvenient; as such, there are a number of convenience // extension methods available via `ConvUtil` and `ConvAsUtil`. assert_eq!(400u16.approx(), Err::(PosOverflow(400))); assert_eq!(400u16.approx_by::(), Ok::(144u8)); assert_eq!(400u16.approx_as::(), Err(PosOverflow(400))); assert_eq!(400u16.approx_as_by::(), Ok(144)); // Integer -> float conversions *can* fail due to limited precision. // Once the continuous range of exactly representable integers is exceeded, the // provided implementations fail with overflow errors. assert_eq!(f32::value_from(16_777_216i32), Ok(16_777_216.0f32)); assert_eq!(f32::value_from(16_777_217i32), Err(RangeError::PosOverflow(16_777_217))); // Float -> integer conversions have to be done using approximations. Although // exact conversions are *possible*, "advertising" this with an implementation // is misleading. // // Note that `DefaultApprox` for float -> integer uses whatever rounding // mode is currently active (*i.e.* whatever `as` would do). assert_eq!(41.0f32.approx(), Ok(41u8)); assert_eq!(41.3f32.approx(), Ok(41u8)); assert_eq!(41.5f32.approx(), Ok(41u8)); assert_eq!(41.8f32.approx(), Ok(41u8)); assert_eq!(42.0f32.approx(), Ok(42u8)); assert_eq!(255.0f32.approx(), Ok(255u8)); assert_eq!(256.0f32.approx(), Err::(FloatError::PosOverflow(256.0))); // Sometimes, it can be useful to saturate the conversion from float to // integer directly, then account for NaN as input separately. The `Saturate` // extension trait exists for this reason. assert_eq!((-23.0f32).approx_as::().saturate(), Ok(0)); assert_eq!(302.0f32.approx_as::().saturate(), Ok(255u8)); assert!(std::f32::NAN.approx_as::().saturate().is_err()); // If you really don't care about the specific kind of error, you can just rely // on automatic conversion to `GeneralErrorKind`. fn too_many_errors() -> Result<(), GeneralErrorKind> { assert_eq!({let r: u8 = try!(0u8.value_into()); r}, 0u8); assert_eq!({let r: u8 = try!(0i8.value_into()); r}, 0u8); assert_eq!({let r: u8 = try!(0i16.value_into()); r}, 0u8); assert_eq!({let r: u8 = try!(0.0f32.approx()); r}, 0u8); Ok(()) } # let _ = too_many_errors(); # } ``` */ #![deny(missing_docs)] #[macro_use] extern crate custom_derive; // Exported macros. pub mod macros; pub use errors::{ NoError, GeneralError, GeneralErrorKind, Unrepresentable, NegOverflow, PosOverflow, FloatError, RangeError, RangeErrorKind, Saturate, UnwrapOk, UnwrapOrInf, UnwrapOrInvalid, UnwrapOrSaturate, }; use std::error::Error; /** Publicly re-exports the most generally useful set of items. 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. */ pub mod prelude { pub use super::{ ApproxFrom, ApproxInto, ValueFrom, ValueInto, GeneralError, GeneralErrorKind, Saturate, UnwrapOk, UnwrapOrInf, UnwrapOrInvalid, UnwrapOrSaturate, ConvUtil, ConvAsUtil, RoundToNearest, RoundToZero, Wrapping, }; } macro_rules! as_item { ($($i:item)*) => {$($i)*}; } macro_rules! item_for_each { ( $( ($($arg:tt)*) ),* $(,)* => { $($exp:tt)* } ) => { macro_rules! body { $($exp)* } $( body! { $($arg)* } )* }; } pub mod errors; pub mod misc; mod impls; /** 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. Where possible, prefer *implementing* this trait over `ApproxInto`, but prefer *using* `ApproxInto` for generic constraints. # Details All implementations of this trait must provide a conversion that can be separated into two logical steps: an approximation transform, and a representation transform. 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. 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. 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? 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. */ pub trait ApproxFrom: Sized where Scheme: ApproxScheme { /// The error type produced by a failed conversion. type Err: Error; /// Convert the given value into an approximately equivalent representation. fn approx_from(src: Src) -> Result; } impl ApproxFrom for Src where Scheme: ApproxScheme { type Err = NoError; fn approx_from(src: Src) -> Result { Ok(src) } } /** This is the dual of `ApproxFrom`; see that trait for information. Where possible, prefer *using* this trait over `ApproxFrom` for generic constraints, but prefer *implementing* `ApproxFrom`. */ pub trait ApproxInto where Scheme: ApproxScheme { /// The error type produced by a failed conversion. type Err: Error; /// Convert the subject into an approximately equivalent representation. fn approx_into(self) -> Result; } impl ApproxInto for Src where Dst: ApproxFrom, Scheme: ApproxScheme, { type Err = Dst::Err; fn approx_into(self) -> Result { ApproxFrom::approx_from(self) } } /** This trait is used to mark approximation scheme types. */ pub trait ApproxScheme {} /** The "default" approximation scheme. This scheme does whatever would generally be expected of a lossy conversion, assuming no additional context or instruction is given. This is a double-edged sword: it has the loosest semantics, but is far more likely to exist than more complicated approximation schemes. */ pub enum DefaultApprox {} impl ApproxScheme for DefaultApprox {} /** This scheme is used to convert a value by "wrapping" it into a narrower range. 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. */ pub enum Wrapping {} impl ApproxScheme for Wrapping {} /** This scheme is used to convert a value by rounding it to the nearest representable value, with ties rounding away from zero. */ pub enum RoundToNearest {} impl ApproxScheme for RoundToNearest {} /** This scheme is used to convert a value by rounding it toward negative infinity to the nearest representable value. */ pub enum RoundToNegInf {} impl ApproxScheme for RoundToNegInf {} /** This scheme is used to convert a value by rounding it toward positive infinity to the nearest representable value. */ pub enum RoundToPosInf {} impl ApproxScheme for RoundToPosInf {} /** This scheme is used to convert a value by rounding it toward zero to the nearest representable value. */ pub enum RoundToZero {} impl ApproxScheme for RoundToZero {} /** This trait is used to perform a conversion between different semantic types which might fail. Where possible, prefer *implementing* this trait over `TryInto`, but prefer *using* `TryInto` for generic constraints. # Details 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. */ pub trait TryFrom: Sized { /// The error type produced by a failed conversion. type Err: Error; /// Convert the given value into the subject type. fn try_from(src: Src) -> Result; } impl TryFrom for Src { type Err = NoError; fn try_from(src: Src) -> Result { Ok(src) } } /** This is the dual of `TryFrom`; see that trait for information. Where possible, prefer *using* this trait over `TryFrom` for generic constraints, but prefer *implementing* `TryFrom`. */ pub trait TryInto { /// The error type produced by a failed conversion. type Err: Error; /// Convert the subject into the destination type. fn try_into(self) -> Result; } impl TryInto for Src where Dst: TryFrom { type Err = Dst::Err; fn try_into(self) -> Result { TryFrom::try_from(self) } } /** This trait is used to perform an exact, value-preserving conversion. Where possible, prefer *implementing* this trait over `ValueInto`, but prefer *using* `ValueInto` for generic constraints. # Details 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. */ pub trait ValueFrom: Sized { /// The error type produced by a failed conversion. type Err: Error; /// Convert the given value into an exactly equivalent representation. fn value_from(src: Src) -> Result; } impl ValueFrom for Src { type Err = NoError; fn value_from(src: Src) -> Result { Ok(src) } } /** This is the dual of `ValueFrom`; see that trait for information. Where possible, prefer *using* this trait over `ValueFrom` for generic constraints, but prefer *implementing* `ValueFrom`. */ pub trait ValueInto { /// The error type produced by a failed conversion. type Err: Error; /// Convert the subject into an exactly equivalent representation. fn value_into(self) -> Result; } impl ValueInto for Src where Dst: ValueFrom { type Err = Dst::Err; fn value_into(self) -> Result { ValueFrom::value_from(self) } } /** This extension trait exists to simplify using various conversions. 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. See also the [`ConvAsUtil`](./trait.ConvAsUtil.html) trait. > **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. */ pub trait ConvUtil { /// Approximate the subject to a given type with the default scheme. fn approx_as(self) -> Result where Self: Sized + ApproxInto { self.approx_into() } /// Approximate the subject to a given type with a specific scheme. fn approx_as_by(self) -> Result where Self: Sized + ApproxInto, Scheme: ApproxScheme, { self.approx_into() } /// Convert the subject to a given type. fn into_as(self) -> Dst where Self: Sized + Into { self.into() } /// Attempt to convert the subject to a given type. fn try_as(self) -> Result where Self: Sized + TryInto { self.try_into() } /// Attempt a value conversion of the subject to a given type. fn value_as(self) -> Result where Self: Sized + ValueInto { self.value_into() } } impl ConvUtil for T {} /** This extension trait exists to simplify using various conversions. 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. The destination type is inferred from context. See also the [`ConvUtil`](./trait.ConvUtil.html) trait. > **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. */ pub trait ConvAsUtil { /// Approximate the subject with the default scheme. fn approx(self) -> Result where Self: Sized + ApproxInto { self.approx_into() } /// Approximate the subject with a specific scheme. fn approx_by(self) -> Result where Self: Sized + ApproxInto, Scheme: ApproxScheme, { self.approx_into() } } impl ConvAsUtil for T {} conv-0.3.3/src/macros.rs00006440000000000000000000007605125611063340013302 0ustar0000000000000000/*! This module provides convenience macros to help with implementing the conversion traits. # `TryFrom!` ```ignore macro_rules! TryFrom { (($target:ty) $enum:item) => { ... }; } ``` 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`). 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)). It is compatible with the [`custom_derive!`](https://crates.io/crates/custom_derive) macro. ## Example Using `custom_derive!`: ``` #[macro_use] extern crate conv; #[macro_use] extern crate custom_derive; custom_derive! { #[derive(Debug, PartialEq, TryFrom(i32))] enum Colours { Red = 0, Green = 5, Blue } } fn main() { use conv::{TryFrom, Unrepresentable}; assert_eq!(Colours::try_from(0), Ok(Colours::Red)); assert_eq!(Colours::try_from(1), Err(Unrepresentable(1))); assert_eq!(Colours::try_from(5), Ok(Colours::Green)); assert_eq!(Colours::try_from(6), Ok(Colours::Blue)); assert_eq!(Colours::try_from(7), Err(Unrepresentable(7))); } ``` The above is equivalent to the following: ``` #[macro_use] extern crate conv; #[derive(Debug, PartialEq)] enum Colours { Red = 0, Green = 5, Blue } TryFrom! { (i32) enum Colours { Red = 0, Green = 5, Blue } } # fn main() {} ``` */ /** See the documentation for the [`macros`](./macros/index.html#tryfrom!) module for details. */ #[macro_export] macro_rules! TryFrom { (($prim:ty) $(pub)* enum $name:ident { $($body:tt)* }) => { TryFrom! { @collect_variants ($name, $prim), ($($body)*,) -> () } }; ( @collect_variants ($name:ident, $prim:ty), ($(,)*) -> ($($var_names:ident,)*) ) => { impl $crate::TryFrom<$prim> for $name { type Err = $crate::errors::Unrepresentable<$prim>; fn try_from(src: $prim) -> Result<$name, Self::Err> { $( if src == $name::$var_names as $prim { return Ok($name::$var_names); } )* Err($crate::errors::Unrepresentable(src)) } } }; ( @collect_variants $fixed:tt, (#[$_attr:meta] $($tail:tt)*) -> $var_names:tt ) => { TryFrom! { @skip_meta $fixed, ($($tail)*) -> $var_names } }; ( @collect_variants $fixed:tt, ($var:ident $(= $_val:expr)*, $($tail:tt)*) -> ($($var_names:tt)*) ) => { TryFrom! { @collect_variants $fixed, ($($tail)*) -> ($($var_names)* $var,) } }; ( @collect_variants ($name:ident), ($var:ident $_struct:tt, $($tail:tt)*) -> ($($var_names:tt)*) ) => { const _error: () = concat!( "cannot derive TryFrom for ", stringify!($name), ", due to non-unitary variant ", stringify!($var), "." ); }; ( @skip_meta $fixed:tt, (#[$_attr:meta] $($tail:tt)*) -> $var_names:tt ) => { TryFrom! { @skip_meta $fixed, ($($tail)*) -> $var_names } }; ( @skip_meta $fixed:tt, ($var:ident $($tail:tt)*) -> $var_names:tt ) => { TryFrom! { @collect_variants $fixed, ($var $($tail)*) -> $var_names } }; } conv-0.3.3/src/misc.rs00006440000000000000000000003651126061544740012756 0ustar0000000000000000/*! This module defines some additional traits not *directly* tied to conversions. */ /** This trait indicates that values of a type can be logically "saturated". This is used by the `errors::UnwrapOrSaturate` extension trait. */ pub trait Saturated { /// Returns the type's saturated, maximum value. fn saturated_max() -> Self; /// Returns the type's saturated, minimum value. fn saturated_min() -> Self; } item_for_each! { (i8), (i16), (i32), (i64), (u8), (u16), (u32), (u64), (isize), (usize) => { ($ity:ident) => { impl Saturated for $ity { #[inline] fn saturated_max() -> Self { ::std::$ity::MAX } #[inline] fn saturated_min() -> Self { ::std::$ity::MIN } } }; } } /** This trait indicates that a type has an "invalid" sentinel value. This is used by the `errors::UnwrapOrInvalid` extension trait. */ pub trait InvalidSentinel { /// Returns the type's "invalid" sentinel value. fn invalid_sentinel() -> Self; } item_for_each! { (f32), (f64) => { ($ity:ident) => { impl InvalidSentinel for $ity { #[inline] fn invalid_sentinel() -> Self { ::std::$ity::NAN } } }; } } /** This trait indicates that a type has positive and negative "infinity" values. This is used by the `errors::UnwrapOrInf` extension trait. */ pub trait SignedInfinity { /// Returns the type's positive infinity value. fn neg_infinity() -> Self; /// Returns the type's negative infinity value. fn pos_infinity() -> Self; } item_for_each! { (f32), (f64) => { ($ity:ident) => { impl SignedInfinity for $ity { #[inline] fn neg_infinity() -> Self { ::std::$ity::NEG_INFINITY } #[inline] fn pos_infinity() -> Self { ::std::$ity::INFINITY } } }; } } conv-0.3.3/tests/conv_utils.rs00006440000000000000000000002217126061544740014560 0ustar0000000000000000#[macro_use] extern crate conv; use conv::prelude::*; #[test] fn test_approx() { use conv::DefaultApprox; assert_eq!((1.5f32).approx(), Ok(1i32)); assert_eq!((1.5f32).approx_by::(), Ok(1)); assert_eq!((1.5f32).approx_as::(), Ok(1)); assert_eq!((1.5f32).approx_as_by::(), Ok(1)); } #[test] fn test_into() { let v = "ABC".into_as::>(); assert_eq!(&*v, &[0x41, 0x42, 0x43]); } #[test] fn test_try() { #[derive(PartialEq, Debug)] enum ItAintRight { BabeNo, NoNo } TryFrom! { (u8) enum ItAintRight { BabeNo, NoNo } } assert_eq!(0u8.try_as::(), Ok(ItAintRight::BabeNo)); assert_eq!(1u8.try_as::(), Ok(ItAintRight::NoNo)); assert_eq!(2u8.try_as::(), Err(conv::Unrepresentable(2))); } #[test] fn test_value() { assert_eq!((123u32).value_as::(), Ok(123)); } #[test] fn test_whizzo() { use conv::errors::Unrepresentable; assert_eq!((-1.0f32).approx_as::().saturate(), Ok::<_, Unrepresentable<_>>(0u8)); assert_eq!((-1i32).value_as::().saturate().unwrap_ok(), 0u8); } conv-0.3.3/tests/derive_try_from.rs00006440000000000000000000002446125611044650015571 0ustar0000000000000000#[macro_use] extern crate conv; use conv::{TryFrom, Unrepresentable}; #[derive(Debug, PartialEq)] enum Get { Up, Down, AllAround } TryFrom! { (u8) enum Get { Up, /// And Down, /** And */ AllAround } } #[derive(Debug, PartialEq)] enum GottaGo { GetAway, Fast = 9000, Faster = 9001 } TryFrom! { (u16) enum GottaGo { GetAway, Fast = 9000, /// This show was stupid. Faster = 9001 } } #[test] fn test_try_from() { assert_eq!(Get::try_from(0u8), Ok(Get::Up)); assert_eq!(Get::try_from(1u8), Ok(Get::Down)); assert_eq!(Get::try_from(2u8), Ok(Get::AllAround)); assert_eq!(Get::try_from(3u8), Err(Unrepresentable(3u8))); assert_eq!(GottaGo::try_from(0u16), Ok(GottaGo::GetAway)); assert_eq!(GottaGo::try_from(1u16), Err(Unrepresentable(1u16))); assert_eq!(GottaGo::try_from(2u16), Err(Unrepresentable(2u16))); assert_eq!(GottaGo::try_from(3u16), Err(Unrepresentable(3u16))); assert_eq!(GottaGo::try_from(8999u16), Err(Unrepresentable(8999u16))); assert_eq!(GottaGo::try_from(9000u16), Ok(GottaGo::Fast)); assert_eq!(GottaGo::try_from(9001u16), Ok(GottaGo::Faster)); assert_eq!(GottaGo::try_from(9002u16), Err(Unrepresentable(9002u16))); } conv-0.3.3/tests/lang_char.rs00006440000000000000000000006542126714343030014310 0ustar0000000000000000extern crate conv; #[macro_use] mod util; use conv::*; use conv::PosOverflow as Of; use conv::Unrepresentable as Ur; macro_rules! check { (@ $from:ty, $to:ty=> $(;)*) => {}; (@ $from:ty, $to:ty=> try cident; $($tail:tt)*) => { check!(@ $from, $to=> try v: '\x00';); check!(@ $from, $to=> try v: '\x01';); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> try uident; $($tail:tt)*) => { check!(@ $from, $to=> try v: 0;); check!(@ $from, $to=> try v: 1;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> try v: $src:expr, !$dst:expr; $($tail:tt)*) => { { let src: $from = $src; let dst: Result<$to, _> = src.try_into(); assert_eq!(dst, Err($dst(src))); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> try v: $src:expr; $($tail:tt)*) => { { let src: $from = $src; let dst: Result<$to, _> = src.try_into(); assert_eq!(dst, Ok($src as $to)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qt: *; $($tail:tt)*) => { { extern crate quickcheck; fn property(v: $from) -> bool { let dst: Result<$to, _> = v.try_into(); dst == Ok(v as $to) } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; ($from:ty, $to:ty=> $($tail:tt)*) => { check! { @ $from, $to=> $($tail)*; } }; } #[test] fn test_i_to_c() { check!(u8, char => try uident; qt: *); /* `char` is a pain because `u8` is the *only* type you can cast directly from. So, the `check!` macro is *basically useless*. Also, `char` has a great big hole in the middle, which makes things more interesting. Instead, we're just going to make sure that the conversions *exist* and have the expected error type. */ macro_rules! check_i_to_c { ($($ts:ty),* $(,)*) => { $( { let v: $ts = 0; let r: Result> = TryFrom::try_from(v); assert_eq!(r, Ok('\x00')); } )* }; } check_i_to_c!(i8, i16, i32, i64, isize, u16, u32, u64, usize); } #[test] fn test_c_to_i() { check!(char, i8=> try cident; try v: '\u{80}', !Of; ); check!(char, i16=> try cident; try v: '\u{8000}', !Of; ); check!(char, i32=> try cident;); check!(char, i64=> try cident;); check!(char, u8=> try cident; try v: '\u{100}', !Of; ); check!(char, u16=> try cident; try v: '\u{10000}', !Of; ); check!(char, u32=> try cident;); check!(char, u64=> try cident;); for_bitness! { 32 { check!(char, isize=> try cident; try v: '\u{10ffff}'; ); check!(char, usize=> try cident;); } 64 { check!(char, i64=> try cident;); check!(char, u64=> try cident;); } } } conv-0.3.3/tests/lang_floats.rs00006440000000000000000000004350126754102300014653 0ustar0000000000000000extern crate conv; #[macro_use] mod util; use conv::*; use conv::FloatError::NegOverflow as FU; use conv::FloatError::PosOverflow as FO; #[test] fn test_f32() { check!(f32, f32=> fident; qv: *;); check!(f32, f64=> fident; qv: *;); } #[test] fn test_f32_to_int() { check!(f32, i8=> sidenta; qa: i8=> a: -129.0, !FU; a: 128.0, !FO;); check!(f32, i16=> sidenta; qa: i16=> a: -32_769.0, !FU; a: 32_768.0, !FO;); check!(f32, i32=> sidenta; qa: i32=> a: -2.1474836e9, -2147483648; a: 2.1474835e9, 2147483520; a: -2_147_500_000.0, !FU; a: 2_147_500_000.0, !FO;); check!(f32, i64=> sidenta; qa: i64=> a: -9.223372e18, -9223372036854775808; a: 9.2233715e18, 9223371487098961920; a: -9_223_373_000_000_000_000.0, !FU; a: 9_223_373_000_000_000_000.0, !FO;); check!(f32, u8=> uidenta; qa: u8=> a: -1.0, !FU; a: 256.0, !FO;); check!(f32, u16=> uidenta; qa: u16=> a: -1.0, !FU; a: 65_536.0, !FO;); check!(f32, u32=> uidenta; qa: u32=> a: 4.294967e9, 4294967040; a: -1.0, !FU; a: 4_294_968_000.0, !FO;); check!(f32, u64=> uidenta; qa: u64=> a: 1.8446743e19, 18446742974197923840; a: -1.0, !FU; a: 18_446_746_000_000_000_000.0, !FO;); } #[test] fn test_f64_to_int() { check!(f64, i8=> sidenta; qa: i8=> a: -129.0, !FU; a: 128.0, !FO;); check!(f64, i16=> sidenta; qa: i16=> a: -32_769.0, !FU; a: 32_768.0, !FO;); check!(f64, i32=> sidenta; qa: i32=> a: -2_147_483_649.0, !FU; a: 2_147_483_648.0, !FO;); check!(f64, i64=> sidenta; qa: i64=> a: -9.223372036854776e18, -9223372036854775808; a: 9.223372036854775e18, 9223372036854774784; a: -9_223_372_036_854_778_000.0, !FU; a: 9_223_372_036_854_778_000.0, !FO;); check!(f64, u8=> uidenta; qa: u8=> a: -1.0, !FU; a: 256.0, !FO;); check!(f64, u16=> uidenta; qa: u16=> a: -1.0, !FU; a: 65_536.0, !FO;); check!(f64, u32=> uidenta; qa: u32=> a: -1.0, !FU; a: 4_294_967_296.0, !FO;); check!(f64, u64=> uidenta; qa: u64=> a: 1.844674407370955e19; a: -1.0, !FU; a: 18_446_744_073_709_560_000.0, !FO;); } #[test] fn test_f64() { check!(f64, f32=> fidenta; qa: *;); check!(f64, f64=> fident; qv: *;); } conv-0.3.3/tests/lang_ints.rs00006440000000000000000000030423126714343030014343 0ustar0000000000000000extern crate conv; #[macro_use] mod util; use conv::*; use conv::NegOverflow as Uf; use conv::PosOverflow as Of; use conv::RangeError::NegOverflow as RU; use conv::RangeError::PosOverflow as RO; #[test] fn test_i8() { check!(i8, i8=> sident; qv: *; qa: *; qaW: *); check!(i8, i16=> sident; qv: *; qa: *; qaW: *); check!(i8, i32=> sident; qv: *; qa: *; qaW: *); check!(i8, i64=> sident; qv: *; qa: *; qaW: *); check!(i8, u8=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i8, u16=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i8, u32=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i8, u64=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i8, isize=> sident; qv: *; qa: *; qaW: *); check!(i8, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } #[test] fn test_i16() { check!(i16, i8=> sident; qv: i8=> qa: i8=> qaW: *; v: -129, !RU; v: 128, !RO; ); check!(i16, i16=> sident; qv: *; qa: *; qaW: *); check!(i16, i32=> sident; qv: *; qa: *; qaW: *); check!(i16, i64=> sident; qv: *; qa: *; qaW: *); check!(i16, u8=> uident; qv: u8=> qa: +; qaW: *; v: -1, !RU; ); check!(i16, u16=> uident; qv: u16, i16=> qa: +; qaW: *; v: -1, !Uf; ); check!(i16, u32=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i16, u64=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i16, isize=> sident; qv: *; qa: *; qaW: *); check!(i16, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } #[test] fn test_i32() { check!(i32, i8=> sident; qv: i8=> qa: i8=> qaW: *; v: -129, !RU; v: 128, !RO; ); check!(i32, i16=> sident; qv: i16=> qa: i16=> qaW: *; v: -32_769, !RU; v: 32_768, !RO; ); check!(i32, i32=> sident; qv: *; qa: *; qaW: *); check!(i32, i64=> sident; qv: *; qa: *; qaW: *); check!(i32, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: -1, !RU; ); check!(i32, u16=> uident; qv: u16=> qa: u16=> qaW: *; v: -1, !RU; ); check!(i32, u32=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(i32, u64=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); for_bitness! { 32 { check!(i32, isize=> sident; qv: *; qa: *; qaW: *); check!(i32, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } 64 { check!(i32, isize=> sident; qv: *; qa: *; qaW: *); check!(i32, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } } } #[test] fn test_i64() { check!(i64, i8=> sident; qv: i8=> qa: i8=> qaW: *; v: -129, !RU; v: 128, !RO; ); check!(i64, i16=> sident; qv: i16=> qa: i16=> qaW: *; v: -32_769, !RU; v: 32_768, !RO; ); check!(i64, i32=> sident; qv: i32=> qa: i32=> qaW: *; v: -2_147_483_649, !RU; v: 2_147_483_648, !RO; ); check!(i64, i64=> sident; qv: *; qa: *; qaW: *; ); check!(i64, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: -1, !RU; ); check!(i64, u16=> uident; qv: u16=> qa: u16=> qaW: *; v: -1, !RU; ); check!(i64, u32=> uident; qv: u32=> qa: u32=> qaW: *; v: -1, !RU; ); check!(i64, u64=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); for_bitness! { 32 { check!(i64, isize=> sident; qv: isize=> qa: isize=> qaW: *; v: -2_147_483_649, !RU; v: 2_147_483_648, !RO; ); check!(i64, usize=> uident; qv: usize=> qa: usize=> qaW: *; v: -1, !RU; v: 4_294_967_296, !RO; ); } 64 { check!(i64, isize=> sident; qv: *; qa: *; qaW: *; ); check!(i64, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } } } #[test] fn test_u8() { check!(u8, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; v: 127; v: 128, !Of; ); check!(u8, i16=> uident; qv: *; qa: *; qaW: *); check!(u8, i32=> uident; qv: *; qa: *; qaW: *); check!(u8, i64=> uident; qv: *; qa: *; qaW: *); check!(u8, u8=> uident; qv: *; qa: *; qaW: *); check!(u8, u16=> uident; qv: *; qa: *; qaW: *); check!(u8, u32=> uident; qv: *; qa: *; qaW: *); check!(u8, u64=> uident; qv: *; qa: *; qaW: *); check!(u8, isize=> uident; qv: *; qa: *; qaW: *); check!(u8, usize=> uident; qv: *; qa: *; qaW: *); } #[test] fn test_u16() { check!(u16, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; v: 128, !Of; ); check!(u16, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; v: 32_768, !Of; ); check!(u16, i32=> uident; qv: *; qa: *; qaW: *); check!(u16, i64=> uident; qv: *; qa: *; qaW: *); check!(u16, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: 256, !Of; ); check!(u16, u16=> uident; qv: *; qa: *; qaW: *); check!(u16, u32=> uident; qv: *; qa: *; qaW: *); check!(u16, u64=> uident; qv: *; qa: *; qaW: *); check!(u16, isize=> uident; qv: *; qa: *; qaW: *); check!(u16, usize=> uident; qv: *; qa: *; qaW: *); } #[test] fn test_u32() { check!(u32, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; v: 128, !Of; ); check!(u32, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; v: 32_768, !Of; ); check!(u32, i32=> uident; qv: +i32=> qa: +i32=> qaW: *; v: 2_147_483_648, !Of; ); check!(u32, i64=> uident; qv: *; qa: *; qaW: *); check!(u32, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: 256, !Of; ); check!(u32, u16=> uident; qv: u16=> qa: u16=> qaW: *; v: 65_536, !Of; ); check!(u32, u32=> uident; qv: *; qa: *; qaW: *); check!(u32, u64=> uident; qv: *; qa: *; qaW: *); for_bitness! { 32 { check!(u32, isize=> uident; qv: +isize=> qa: +isize=> qaW: *; v: 2_147_483_647; v: 2_147_483_648, !Of; ); check!(u32, usize=> uident; qv: *; qa: *; qaW: *); } 64 { check!(u32, isize=> uident; qv: *; qa: *; qaW: *); check!(u32, usize=> uident; qv: *; qa: *; qaW: *); } } } #[test] fn test_u64() { check!(u64, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; v: 128, !Of; ); check!(u64, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; v: 32_768, !Of; ); check!(u64, i32=> uident; qv: +i32=> qa: +i32=> qaW: *; v: 2_147_483_648, !Of; ); check!(u64, i64=> uident; qv: +i64=> qa: +i64=> qaW: *; v: 9_223_372_036_854_775_808, !Of; ); check!(u64, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: 256, !Of; ); check!(u64, u16=> uident; qv: u16=> qa: u16=> qaW: *; v: 65_536, !Of; ); check!(u64, u32=> uident; qv: u32=> qa: u32=> qaW: *; v: 4_294_967_296, !Of; ); check!(u64, u64=> uident; qv: *; qa: *; qaW: *); for_bitness! { 32 { check!(u64, isize=> uident; qv: +isize=> qa: +isize=> qaW: *; v: 2_147_483_648, !Of; ); check!(u64, usize=> uident; qv: usize=> qa: usize=> qaW: *; v: 4_294_967_296, !Of; ); } 64 { check!(u64, isize=> uident; qv: +i64=> qa: +i64=> qaW: *; v: 9_223_372_036_854_775_808, !Of; ); check!(u64, usize=> uident; qv: *; qa: *; qaW: *); } } } #[test] fn test_isize() { check!(isize, i8=> sident; qv: i8=> qa: i8=> qaW: *; v: -129, !RU; v: 128, !RO; ); check!(isize, i16=> sident; qv: i16=> qa: i16=> qaW: *; v: -32_769, !RU; v: 32_768, !RO; ); check!(isize, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: -1, !RU; v: 256, !RO; ); check!(isize, u16=> uident; qv: u16=> qa: u16=> qaW: *; v: -1, !RU; v: 65_536, !RO; ); check!(isize, isize=> sident; qv: *; qa: *; qaW: *); for_bitness! { 32 { check!(isize, i32=> sident; qv: *; qa: *; qaW: *); check!(isize, i64=> sident; qv: *; qa: *; qaW: *); check!(isize, u32=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(isize, u64=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(isize, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } 64 { check!(isize, i32=> sident; qv: *; qa: *; qaW: *); check!(isize, i64=> sident; qv: *; qa: *; qaW: *); check!(isize, u32=> uident; qv: u32=> qa: u32=> qaW: *; v: -1, !RU; v: 4_294_967_296, !RO; ); check!(isize, u64=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); check!(isize, usize=> uident; qv: +; qa: +; qaW: *; v: -1, !Uf; ); } } } #[test] fn test_usize() { check!(usize, i8=> uident; qv: +i8=> qa: +i8=> qaW: *; v: 128, !Of; ); check!(usize, i16=> uident; qv: +i16=> qa: +i16=> qaW: *; v: 32_768, !Of; ); check!(usize, u8=> uident; qv: u8=> qa: u8=> qaW: *; v: 256, !Of; ); check!(usize, u16=> uident; qv: u16=> qa: u16=> qaW: *; v: 65_536, !Of; ); check!(usize, usize=> uident; qv: *; qa: *; qaW: *); for_bitness! { 32 { check!(usize, i32=> uident; qv: +i32=> qa: +i32=> qaW: *); check!(usize, i64=> uident; qv: *; qa: *; qaW: *); check!(usize, u32=> uident; qv: *; qa: *; qaW: *); check!(usize, u64=> uident; qv: *; qa: *; qaW: *); check!(usize, isize=> uident; qv: +isize=> qa: +isize=> qaW: *); } 64 { check!(usize, i32=> uident; qv: +i32=> qa: +i32=> qaW: *); check!(usize, i64=> uident; qv: +i64=> qa: +i64=> qaW: *); check!(usize, u32=> uident; qv: u32=> qa: u32=> qaW: *; v: 4_294_967_296, !Of; ); check!(usize, u64=> uident; qv: *; qa: *; qaW: *); check!(usize, isize=> uident; qv: +isize=> qa: +isize=> qaW: *); } } } #[test] fn test_i_to_f() { check!(i8, f32=> sident; qv: *; qa: *); check!(i16, f32=> sident; qv: *; qa: *); check!(i32, f32=> sident; qv: (+-16_777_216); qa: *; v: -16_777_217, !RU; v: 16_777_217, !RO; ); check!(i64, f32=> sident; qv: (+-16_777_216); qa: *; v: -16_777_217, !RU; v: 16_777_217, !RO; ); check!(isize, f32=> sident; qv: (+-16_777_216); qa: *; v: -16_777_217, !RU; v: 16_777_217, !RO; ); check!(u8, f32=> uident; qv: *; qa: *); check!(u16, f32=> uident; qv: *; qa: *); check!(u32, f32=> uident; qv: (, 16_777_216); qa: *; v: 16_777_217, !Of; ); check!(u64, f32=> uident; qv: (, 16_777_216); qa: *; v: 16_777_217, !Of; ); check!(usize, f32=> uident; qv: (, 16_777_216); qa: *; v: 16_777_217, !Of; ); check!(i8, f64=> sident; qv: *; qa: *); check!(i16, f64=> sident; qv: *; qa: *); check!(i32, f64=> sident; qv: *; qa: *); check!(i64, f64=> sident; qv: (+-9_007_199_254_740_992); qa: *; v: -9_007_199_254_740_993, !RU; v: 9_007_199_254_740_993, !RO; ); for_bitness! { 32 { check!(isize, f64=> sident; qv: *; qa: *); } 64 { check!(i64, f64=> sident; qv: (+-9_007_199_254_740_992); qa: *; v: -9_007_199_254_740_993, !RU; v: 9_007_199_254_740_993, !RO; ); } } check!(u8, f64=> uident; qv: *; qa: *); check!(u16, f64=> uident; qv: *; qa: *); check!(u32, f64=> uident; qv: *; qa: *); check!(u64, f64=> uident; qv: (, 9_007_199_254_740_992); qa: *; v: 9_007_199_254_740_993, !Of; ); for_bitness! { 32 { check!(usize, f64=> uident; qv: *; qa: *); } 64 { check!(u64, f64=> uident; qv: (, 9_007_199_254_740_992); qa: *; v: 9_007_199_254_740_993, !Of; ); } } } conv-0.3.3/tests/unwraps.rs00006440000000000000000000002303125765772260014100 0ustar0000000000000000extern crate conv; #[macro_use] mod util; use conv::*; macro_rules! cty { ($e:expr, $t:ty) => { { let v: $t = $e; v } }; } #[test] fn test_unwraps() { assert_eq!(cty!(0i16.value_into().unwrap(), i32), 0); assert_eq!(cty!(127i16.value_into().unwrap(), i8), 127); assert_eq!(cty!(128i16.value_into().unwrap_or_saturate(), i8), 127); assert_eq!(cty!(128i16.approx().unwrap_or_saturate(), i8), 127); assert_eq!(cty!(128i16.approx_by::().unwrap_or_saturate(), i8), -128); assert_eq!(cty!(16_777_216i32.value_into().unwrap(), f32), 16_777_216.0); assert_eq!(cty!(16_777_216i32.value_into().unwrap_or_inf(), f32), 16_777_216.0); assert_eq!(cty!(16_777_217i32.value_into().unwrap_or_inf(), f32), std::f32::INFINITY); assert_eq!(cty!((-16_777_217i32).value_into().unwrap_or_inf(), f32), std::f32::NEG_INFINITY); assert_eq!(cty!(16_777_216i32.value_into().unwrap_or_invalid(), f32), 16_777_216.0); assert!(cty!(16_777_217i32.value_into().unwrap_or_invalid(), f32).is_nan()); assert!(cty!((-16_777_217i32).value_into().unwrap_or_invalid(), f32).is_nan()); assert_eq!(cty!(0u8.value_into().unwrap_ok(), u16), 0); } conv-0.3.3/tests/use_in_generics.rs00006440000000000000000000000453126072317350015531 0ustar0000000000000000//! Are conversions easily usable in generic code? extern crate conv; use conv::prelude::*; #[test] fn test_generic_unwrap() { fn do_conv(t: T) -> U where T: ValueInto { t.value_into().unwrap() } assert_eq!({let x: u8 = do_conv(42i32); x}, 42u8); } conv-0.3.3/tests/util/mod.rs00006440000000000000000000043720126754066100014131 0ustar0000000000000000macro_rules! SL { ($($tts:tt)*) => { stringify!($($tts)*) }; } macro_rules! as_expr { ($e:expr) => {$e}; } macro_rules! check { (@ $from:ty, $to:ty=> $(;)*) => {}; (@ $from:ty, $to:ty=> cident; $($tail:tt)*) => { check!(@ $from, $to=> v: '\x00';); check!(@ $from, $to=> v: '\x01';); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> uident; $($tail:tt)*) => { check!(@ $from, $to=> v: 0;); check!(@ $from, $to=> v: 1;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> sident; $($tail:tt)*) => { check!(@ $from, $to=> v: -1;); check!(@ $from, $to=> v: 0;); check!(@ $from, $to=> v: 1;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> fident; $($tail:tt)*) => { check!(@ $from, $to=> v: -1.0;); check!(@ $from, $to=> v: 0.0;); check!(@ $from, $to=> v: 1.0;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> uidenta; $($tail:tt)*) => { check!(@ $from, $to=> a: 0.0;); check!(@ $from, $to=> a: 1.0;); check!(@ $from, $to=> aRTN: 0.00, 0;); check!(@ $from, $to=> aRTN: 0.25, 0;); check!(@ $from, $to=> aRTN: 0.50, 1;); check!(@ $from, $to=> aRTN: 0.75, 1;); check!(@ $from, $to=> aRTN: 1.00, 1;); check!(@ $from, $to=> aRNI: 0.00, 0;); check!(@ $from, $to=> aRNI: 0.25, 0;); check!(@ $from, $to=> aRNI: 0.50, 0;); check!(@ $from, $to=> aRNI: 0.75, 0;); check!(@ $from, $to=> aRNI: 1.00, 1;); check!(@ $from, $to=> aRPI: 0.00, 0;); check!(@ $from, $to=> aRPI: 0.25, 1;); check!(@ $from, $to=> aRPI: 0.50, 1;); check!(@ $from, $to=> aRPI: 0.75, 1;); check!(@ $from, $to=> aRPI: 1.00, 1;); check!(@ $from, $to=> aRTZ: 0.00, 0;); check!(@ $from, $to=> aRTZ: 0.25, 0;); check!(@ $from, $to=> aRTZ: 0.50, 0;); check!(@ $from, $to=> aRTZ: 0.75, 0;); check!(@ $from, $to=> aRTZ: 1.00, 1;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> sidenta; $($tail:tt)*) => { check!(@ $from, $to=> a: -1.0;); check!(@ $from, $to=> a: 0.0;); check!(@ $from, $to=> a: 1.0;); check!(@ $from, $to=> aRTN: -1.00, -1;); check!(@ $from, $to=> aRTN: -0.75, -1;); check!(@ $from, $to=> aRTN: -0.50, -1;); check!(@ $from, $to=> aRTN: -0.25, 0;); check!(@ $from, $to=> aRTN: 0.00, 0;); check!(@ $from, $to=> aRTN: 0.25, 0;); check!(@ $from, $to=> aRTN: 0.50, 1;); check!(@ $from, $to=> aRTN: 0.75, 1;); check!(@ $from, $to=> aRTN: 1.00, 1;); check!(@ $from, $to=> aRNI: -1.00, -1;); check!(@ $from, $to=> aRNI: -0.75, -1;); check!(@ $from, $to=> aRNI: -0.50, -1;); check!(@ $from, $to=> aRNI: -0.25, -1;); check!(@ $from, $to=> aRNI: 0.00, 0;); check!(@ $from, $to=> aRNI: 0.25, 0;); check!(@ $from, $to=> aRNI: 0.50, 0;); check!(@ $from, $to=> aRNI: 0.75, 0;); check!(@ $from, $to=> aRNI: 1.00, 1;); check!(@ $from, $to=> aRPI: -1.00, -1;); check!(@ $from, $to=> aRPI: -0.75, 0;); check!(@ $from, $to=> aRPI: -0.50, 0;); check!(@ $from, $to=> aRPI: -0.25, 0;); check!(@ $from, $to=> aRPI: 0.00, 0;); check!(@ $from, $to=> aRPI: 0.25, 1;); check!(@ $from, $to=> aRPI: 0.50, 1;); check!(@ $from, $to=> aRPI: 0.75, 1;); check!(@ $from, $to=> aRPI: 1.00, 1;); check!(@ $from, $to=> aRTZ: -1.00, -1;); check!(@ $from, $to=> aRTZ: -0.75, 0;); check!(@ $from, $to=> aRTZ: -0.50, 0;); check!(@ $from, $to=> aRTZ: -0.25, 0;); check!(@ $from, $to=> aRTZ: 0.00, 0;); check!(@ $from, $to=> aRTZ: 0.25, 0;); check!(@ $from, $to=> aRTZ: 0.50, 0;); check!(@ $from, $to=> aRTZ: 0.75, 0;); check!(@ $from, $to=> aRTZ: 1.00, 1;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> fidenta; $($tail:tt)*) => { check!(@ $from, $to=> a: -1.0;); check!(@ $from, $to=> a: 0.0;); check!(@ $from, $to=> a: 1.0;); check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> v: $src:expr, !$dst:expr; $($tail:tt)*) => { { println!("? {} => {}, v: {}, !{}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.value_into(); assert_eq!(dst, Err($dst(src))); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> v: $src:expr; $($tail:tt)*) => { { println!("? {} => {}, v: {}", SL!($from), SL!($to), SL!($src)); let src: $from = $src; let dst: Result<$to, _> = src.value_into(); assert_eq!(dst, Ok($src as $to)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: *; $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: *", SL!($from), SL!($to)); fn property(v: $from) -> bool { let dst: Result<$to, _> = v.value_into(); dst == Ok(v as $to) } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: (+-$bound:expr); $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: (+- {})", SL!($from), SL!($to), SL!($bound)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); if !(-$bound as $from <= v) { dst == Err(conv::FloatError::NegOverflow(v)) } else if !(v <= $bound as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: (, $bound:expr); $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: (, {})", SL!($from), SL!($to), SL!($bound)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); if !(v <= $bound as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: +; $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: +", SL!($from), SL!($to)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); if !(0 <= v) { dst == Err(conv::FloatError::NegOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: +$max:ty=> $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: +{}", SL!($from), SL!($to), SL!($max)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); if !(v <= <$max>::max_value() as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: $bound:ty=> $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: {}", SL!($from), SL!($to), SL!($bound)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); if !(<$bound>::min_value() as $from <= v) { dst == Err(conv::FloatError::NegOverflow(v)) } else if !(v <= <$bound>::max_value() as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qv: $min:ty, $max:ty=> $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qv: {}, {}", SL!($from), SL!($to), SL!($min), SL!($max)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.value_into().map_err(From::from); if !(<$min>::min_value() as $from <= v) { dst == Err(conv::FloatError::NegOverflow(v)) } else if !(v <= <$max>::max_value() as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qv {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> a: $src:expr, !$dst:expr; $($tail:tt)*) => { { println!("? {} => {}, a: {}, !{}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.approx_as(); assert_eq!(dst, Err($dst(src))); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> a: $src:expr, $dst:expr; $($tail:tt)*) => { { println!("? {} => {}, a: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.approx_as(); assert_eq!(dst, Ok($dst)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> a: $src:expr; $($tail:tt)*) => { { println!("? {} => {}, a: {}", SL!($from), SL!($to), SL!($src)); let src: $from = $src; let dst: Result<$to, _> = src.approx_as(); assert_eq!(dst, Ok($src as $to)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qa: *; $($tail:tt)*) => { { println!("? {} => {}, qa: *", SL!($from), SL!($to)); extern crate quickcheck; fn property(v: $from) -> bool { let dst: Result<$to, _> = v.approx_as(); dst == Ok(v as $to) } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qa {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qa: +; $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qa: +", SL!($from), SL!($to)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.approx_as().map_err(From::from); if !(0 <= v) { dst == Err(conv::FloatError::NegOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qa {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qa: +$max:ty=> $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qa: +{}", SL!($from), SL!($to), SL!($max)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.approx_as().map_err(From::from); if !(v <= <$max>::max_value() as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qa {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qa: $bound:ty=> $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qa: {}", SL!($from), SL!($to), SL!($bound)); fn property(v: $from) -> bool { let dst: Result<$to, conv::FloatError<_>> = v.approx_as().map_err(From::from); if !(<$bound>::min_value() as $from <= v) { dst == Err(conv::FloatError::NegOverflow(v)) } else if !(v <= <$bound>::max_value() as $from) { dst == Err(conv::FloatError::PosOverflow(v)) } else { dst == Ok(v as $to) } } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qa {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> qaW: *; $($tail:tt)*) => { { extern crate quickcheck; println!("? {} => {}, qaW: *", SL!($from), SL!($to)); fn property(v: $from) -> bool { let dst: Result<$to, _> = v.approx_as_by::<_, Wrapping>(); dst == Ok(v as $to) } let mut qc = quickcheck::QuickCheck::new(); match qc.quicktest(property as fn($from) -> bool) { Ok(_) => (), Err(err) => panic!("qaW {:?}", err) } } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> aRTN: $src:expr, $dst:expr; $($tail:tt)*) => { { println!("? {} => {}, aRTN: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.approx_by::(); assert_eq!(dst, Ok($dst)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> aRNI: $src:expr, $dst:expr; $($tail:tt)*) => { { println!("? {} => {}, aRNI: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.approx_by::(); assert_eq!(dst, Ok($dst)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> aRPI: $src:expr, $dst:expr; $($tail:tt)*) => { { println!("? {} => {}, aRPI: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.approx_by::(); assert_eq!(dst, Ok($dst)); } check!(@ $from, $to=> $($tail)*); }; (@ $from:ty, $to:ty=> aRTZ: $src:expr, $dst:expr; $($tail:tt)*) => { { println!("? {} => {}, aRTZ: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst)); let src: $from = $src; let dst: Result<$to, _> = src.approx_by::(); assert_eq!(dst, Ok($dst)); } check!(@ $from, $to=> $($tail)*); }; ($from:ty, $to:ty=> $($tail:tt)*) => { check! { @ $from, $to=> $($tail)*; } }; } macro_rules! for_bitness { (32 {$($bits32:tt)*} 64 {$($bits64:tt)*}) => { as_expr!( { #[cfg(target_pointer_width="32")] fn for_bitness() { $($bits32)* } #[cfg(target_pointer_width="64")] fn for_bitness() { $($bits64)* } for_bitness() } ) }; }