diff --git a/CHANGELOG.md b/CHANGELOG.md index e63a11a..c50b1f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Version 0.4.0 + +* Breaking change: update error handling to make it more future-proof. + ## Version 0.3.1 * Support Default trait. diff --git a/src/convert.rs b/src/convert.rs index 8df06a0..7d13c9e 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,25 +1,26 @@ -use core::cmp::Eq; use core::convert::{From, TryFrom}; use core::fmt; use std::error; use crate::base::{no_overlap, TwoFloat}; -/// Error indicating invalid conversions to/from `TwoFloat`. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct ConversionError; +/// The error type for `TwoFloat` operations. +#[non_exhaustive] +#[derive(Debug)] +pub enum TwoFloatError { + /// Indicates invalid conversion to/from `TwoFloat` + ConversionError, +} -impl fmt::Display for ConversionError { +impl fmt::Display for TwoFloatError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "invalid TwoFloat conversion") + match self { + Self::ConversionError => write!(f, "invalid TwoFloat conversion"), + } } } -impl error::Error for ConversionError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - None - } -} +impl error::Error for TwoFloatError {} macro_rules! from_conversion { (|$source_i:ident : TwoFloat| -> $dest:tt $code:block) => { @@ -49,7 +50,7 @@ macro_rules! from_conversion { from_conversion!(|value: TwoFloat| -> (f64, f64) { (value.hi, value.lo) }); impl TryFrom<(f64, f64)> for TwoFloat { - type Error = ConversionError; + type Error = TwoFloatError; fn try_from(value: (f64, f64)) -> Result { if no_overlap(value.0, value.1) { @@ -58,7 +59,7 @@ impl TryFrom<(f64, f64)> for TwoFloat { lo: value.1, }) } else { - Err(Self::Error {}) + Err(Self::Error::ConversionError {}) } } } @@ -66,7 +67,7 @@ impl TryFrom<(f64, f64)> for TwoFloat { from_conversion!(|value: TwoFloat| -> [f64; 2] { [value.hi, value.lo] }); impl TryFrom<[f64; 2]> for TwoFloat { - type Error = ConversionError; + type Error = TwoFloatError; fn try_from(value: [f64; 2]) -> Result { if no_overlap(value[0], value[1]) { @@ -75,7 +76,7 @@ impl TryFrom<[f64; 2]> for TwoFloat { lo: value[1], }) } else { - Err(Self::Error {}) + Err(Self::Error::ConversionError {}) } } } @@ -109,12 +110,12 @@ macro_rules! int_convert { } } - from_conversion!(|value: TwoFloat| -> Result<$type, ConversionError> { + from_conversion!(|value: TwoFloat| -> Result<$type, TwoFloatError> { const LOWER_BOUND: f64 = $type::MIN as f64; const UPPER_BOUND: f64 = $type::MAX as f64; let truncated = value.trunc(); if truncated < LOWER_BOUND || truncated > UPPER_BOUND { - Err(ConversionError {}) + Err(Self::Error::ConversionError {}) } else { Ok(truncated.hi() as $type) } @@ -146,7 +147,7 @@ macro_rules! bigint_convert { } } - from_conversion!(|value: TwoFloat| -> Result<$type, ConversionError> { + from_conversion!(|value: TwoFloat| -> Result<$type, TwoFloatError> { const LOWER_BOUND: TwoFloat = TwoFloat { hi: $type::MIN as f64, lo: 0.0, @@ -159,7 +160,7 @@ macro_rules! bigint_convert { let truncated = value.trunc(); if truncated < LOWER_BOUND || truncated > UPPER_BOUND { - Err(ConversionError {}) + Err(Self::Error::ConversionError {}) } else if truncated.hi() == UPPER_BOUND.hi() { Ok($type::MAX - (-truncated.lo() as $type) + 1) } else if truncated.lo() >= 0.0 { diff --git a/src/lib.rs b/src/lib.rs index c0eed45..c464d42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,4 +68,4 @@ mod convert; mod functions; pub use base::{no_overlap, TwoFloat}; -pub use convert::ConversionError; +pub use convert::TwoFloatError; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 57cbd2d..4aae77e 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -2,7 +2,7 @@ use core::convert::TryFrom; use rand::Rng; -use twofloat::{ConversionError, TwoFloat}; +use twofloat::{TwoFloat, TwoFloatError}; pub const TEST_ITERS: usize = 100000; @@ -52,7 +52,7 @@ pub fn get_twofloat(rng: F64Rand) -> TwoFloat { } } -pub fn try_get_twofloat_with_hi(rng: F64Rand, hi: f64) -> Result { +pub fn try_get_twofloat_with_hi(rng: F64Rand, hi: f64) -> Result { if hi == 0.0 { return Ok(TwoFloat::from(0.0)); } @@ -64,10 +64,10 @@ pub fn try_get_twofloat_with_hi(rng: F64Rand, hi: f64) -> Result Result { +pub fn try_get_twofloat_with_lo(rng: F64Rand, lo: f64) -> Result { for _ in 0..10 { let result = TwoFloat::try_from((rng(), lo)); if result.is_ok() { @@ -75,7 +75,7 @@ pub fn try_get_twofloat_with_lo(rng: F64Rand, lo: f64) -> Result bool>(rng: F64Rand, pred: F) -> TwoFloat { diff --git a/tests/convert_tests.rs b/tests/convert_tests.rs index 6259c1d..ea839d5 100644 --- a/tests/convert_tests.rs +++ b/tests/convert_tests.rs @@ -1,7 +1,8 @@ use core::convert::TryFrom; +use core::mem::discriminant; use rand::Rng; -use twofloat::{no_overlap, ConversionError, TwoFloat}; +use twofloat::{no_overlap, TwoFloat, TwoFloatError}; pub mod common; use common::*; @@ -128,28 +129,29 @@ float_test!(f64, from_f64_test, into_f64_test); float_test!(f32, from_f32_test, into_f32_test); fn check_try_from_result( - expected: Result, - result: Result, + expected: &Result, + result: &Result, source: TwoFloat, ) { - if let Ok(expected_value) = expected { - assert!( - result.is_ok(), - "Conversion of {:?} produced error instead of result", - source - ); - assert_eq!( - result.unwrap(), - expected_value, + assert_eq!( + discriminant(expected), + discriminant(result), + "Conversion of {:?} produced unexpected Err/Ok state", + source + ); + match (expected, result) { + (Ok(expected_value), Ok(result_value)) => assert_eq!( + expected_value, result_value, "Conversion of {:?} produced incorrect result", source - ); - } else { - assert!( - result.is_err(), - "Conversion of {:?} produced result instead of error", + ), + (Err(expected_err), Err(result_err)) => assert_eq!( + discriminant(expected_err), + discriminant(result_err), + "Conversion of {:?} produced mismatched error types", source - ); + ), + _ => unreachable!(), } } @@ -168,18 +170,14 @@ macro_rules! from_twofloat_test { let expected = if source.lo() > 0.0 { Ok($type::MIN) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) }; let result = $type::try_from(source); - check_try_from_result(expected, result, source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); }); randomized_test!(from_twofloat_upper_bound, |rng: F64Rand| { @@ -188,21 +186,18 @@ macro_rules! from_twofloat_test { break source; } }; + let expected = if source.lo() < 0.0 { Ok($type::MAX) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) }; - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); }); #[test] @@ -232,15 +227,10 @@ macro_rules! from_twofloat_test { }; let result = $type::try_from(source); - - check_try_from_result(expected, result, source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -266,16 +256,12 @@ macro_rules! from_twofloat_test { }; let expected = Ok(a.trunc() as $type); - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -290,9 +276,9 @@ macro_rules! from_twofloat_test { ); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", + assert!( + result_ref.is_err(), + "Conversion of {:?} produced value instead of error", source ); }); @@ -464,26 +450,21 @@ macro_rules! int64_test { if source.lo() > 0.0 { Ok($type::MIN) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) } } else { if source.lo() > -1.0 { Ok($type::MIN + source.lo().ceil() as $type) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) } }; let result = $type::try_from(source); - - check_try_from_result(expected, result, source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); }); randomized_test!(from_twofloat_upper_bound, |rng: F64Rand| { @@ -495,18 +476,14 @@ macro_rules! int64_test { let expected = if source.lo() < 0.0 { Ok($type::MAX - ((-source.lo().floor()) as $type) + 1) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) }; - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); }); #[test] @@ -546,16 +523,12 @@ macro_rules! int64_test { let source = TwoFloat::try_from((a, b)).unwrap(); let expected = Ok(a as $type); - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -599,16 +572,12 @@ macro_rules! int64_test { } else { Ok(a as $type) }; - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -652,16 +621,12 @@ macro_rules! int64_test { Ok(a as $type - (-b) as $type) } }; - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -677,9 +642,9 @@ macro_rules! int64_test { ); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", + assert!( + result_ref.is_err(), + "Conversion of {:?} produced value instead of error", source ); }); @@ -798,26 +763,21 @@ macro_rules! int128_test { if source.lo() > 0.0 { Ok($type::MIN) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) } } else { if source.lo() > -1.0 { Ok($type::MIN + source.lo().ceil() as $type) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) } }; let result = $type::try_from(source); - - check_try_from_result(expected, result, source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); }); randomized_test!(from_twofloat_upper_bound, |rng: F64Rand| { @@ -829,18 +789,14 @@ macro_rules! int128_test { let expected = if source.lo() < 0.0 { Ok($type::MAX - ((-source.lo().floor()) as $type) + 1) } else { - Err(ConversionError {}) + Err(TwoFloatError::ConversionError {}) }; - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); }); #[test] @@ -880,16 +836,12 @@ macro_rules! int128_test { let source = TwoFloat::try_from((a, b)).unwrap(); let expected = Ok(a as $type); - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -933,16 +885,12 @@ macro_rules! int128_test { } else { Ok(a as $type) }; - let result = $type::try_from(source); - check_try_from_result(expected, result, source); + let result = $type::try_from(source); + check_try_from_result(&expected, &result, source); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", - source - ); + check_try_from_result(&expected, &result_ref, source); } } @@ -958,9 +906,9 @@ macro_rules! int128_test { ); let result_ref = $type::try_from(&source); - assert_eq!( - result, result_ref, - "Different value and reference conversions for {:?}", + assert!( + result_ref.is_err(), + "Conversion of {:?} produced value instead of error", source ); });