use crate::error::{ArithmeticError, Overflow, RangeError, Undefined, Underflow};
pub trait TryAdd<Rhs = Self> {
type Output;
type Error;
fn try_add(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
}
pub trait TryDiv<Rhs = Self> {
type Output;
type Error;
fn try_div(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
}
pub trait TryMul<Rhs = Self> {
type Output;
type Error;
fn try_mul(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
}
pub trait TryNeg {
type Output;
type Error;
fn try_neg(self) -> Result<Self::Output, Self::Error>;
}
pub trait TryRem<Rhs = Self> {
type Output;
type Error;
fn try_rem(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
}
pub trait TrySub<Rhs = Self> {
type Output;
type Error;
fn try_sub(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
}
macro_rules! impl_unary_ref_ops {
{ $(impl $trait:ident::$fn:ident for $ty:ident;)* } => { $(
impl<'a> $trait for &'a $ty {
type Output = $ty;
type Error = <$ty as $trait>::Error;
fn $fn(self) -> Result<$ty, Self::Error> {
$trait::$fn(*self)
}
}
)* };
}
macro_rules! impl_binary_ref_ops {
{ $(impl $trait:ident::$fn:ident for $ty:ident;)* } => { $(
impl<'a> $trait<$ty> for &'a $ty {
type Output = $ty;
type Error = <$ty as $trait>::Error;
fn $fn(self, rhs: $ty) -> Result<$ty, Self::Error> {
$trait::$fn(*self, rhs)
}
}
impl<'r> $trait<&'r $ty> for $ty {
type Output = $ty;
type Error = <$ty as $trait>::Error;
fn $fn(self, rhs: &'r $ty) -> Result<$ty, Self::Error> {
$trait::$fn(self, *rhs)
}
}
impl<'a, 'r> $trait<&'r $ty> for &'a $ty {
type Output = $ty;
type Error = <$ty as $trait>::Error;
fn $fn(self, rhs: &'r $ty) -> Result<$ty, Self::Error> {
$trait::$fn(*self, *rhs)
}
}
)* };
}
macro_rules! impl_int_ops {
($($ty:ident),*) => { $(
impl TryAdd for $ty {
type Output = $ty;
type Error = RangeError;
fn try_add(self, rhs: $ty) -> Result<$ty, RangeError> {
match self.checked_add(rhs) {
None => Err(if self >= 0 {
RangeError::Overflow
} else {
RangeError::Underflow
}),
Some(n) => Ok(n),
}
}
}
impl TryDiv for $ty {
type Output = $ty;
type Error = ArithmeticError;
fn try_div(self, rhs: $ty) -> Result<$ty, ArithmeticError> {
match self.checked_div(rhs) {
None => Err(if rhs == 0 {
ArithmeticError::Undefined
} else {
ArithmeticError::Overflow
}),
Some(n) => Ok(n),
}
}
}
impl TryMul for $ty {
type Output = $ty;
type Error = RangeError;
fn try_mul(self, rhs: $ty) -> Result<$ty, RangeError> {
match self.checked_mul(rhs) {
None => Err(if (self >= 0) == (rhs >= 0) {
RangeError::Overflow
} else {
RangeError::Underflow
}),
Some(n) => Ok(n),
}
}
}
impl TryNeg for $ty {
type Output = $ty;
type Error = Overflow;
fn try_neg(self) -> Result<$ty, Overflow> {
match self.checked_neg() {
None => Err(Overflow),
Some(n) => Ok(n),
}
}
}
impl TryRem for $ty {
type Output = $ty;
type Error = Undefined;
fn try_rem(self, rhs: $ty) -> Result<$ty, Undefined> {
match self.checked_rem(rhs) {
None => if rhs == 0 {
Err(Undefined)
} else {
Ok(0)
},
Some(n) => Ok(n),
}
}
}
impl TrySub for $ty {
type Output = $ty;
type Error = RangeError;
fn try_sub(self, rhs: $ty) -> Result<$ty, RangeError> {
match self.checked_sub(rhs) {
None => Err(if self >= 0 {
RangeError::Overflow
} else {
RangeError::Underflow
}),
Some(n) => Ok(n),
}
}
}
impl_unary_ref_ops! {
impl TryNeg::try_neg for $ty;
}
impl_binary_ref_ops! {
impl TryAdd::try_add for $ty;
impl TryDiv::try_div for $ty;
impl TryMul::try_mul for $ty;
impl TryRem::try_rem for $ty;
impl TrySub::try_sub for $ty;
}
)* };
}
impl_int_ops!(i8, i16, i32, i64, i128, isize);
macro_rules! impl_uint_ops {
($($ty:ident),*) => { $(
impl TryAdd for $ty {
type Output = $ty;
type Error = Overflow;
fn try_add(self, rhs: $ty) -> Result<$ty, Overflow> {
match self.checked_add(rhs) {
None => Err(Overflow),
Some(n) => Ok(n),
}
}
}
impl TryDiv for $ty {
type Output = $ty;
type Error = Undefined;
fn try_div(self, rhs: $ty) -> Result<$ty, Undefined> {
match self.checked_div(rhs) {
None => Err(Undefined),
Some(n) => Ok(n),
}
}
}
impl TryMul for $ty {
type Output = $ty;
type Error = Overflow;
fn try_mul(self, rhs: $ty) -> Result<$ty, Overflow> {
match self.checked_mul(rhs) {
None => Err(Overflow),
Some(n) => Ok(n),
}
}
}
impl TryNeg for $ty {
type Output = $ty;
type Error = Underflow;
fn try_neg(self) -> Result<$ty, Underflow> {
match self.checked_neg() {
None => Err(Underflow),
Some(n) => Ok(n),
}
}
}
impl TryRem for $ty {
type Output = $ty;
type Error = Undefined;
fn try_rem(self, rhs: $ty) -> Result<$ty, Undefined> {
match self.checked_rem(rhs) {
None => Err(Undefined),
Some(n) => Ok(n),
}
}
}
impl TrySub for $ty {
type Output = $ty;
type Error = Underflow;
fn try_sub(self, rhs: $ty) -> Result<$ty, Underflow> {
match self.checked_sub(rhs) {
None => Err(Underflow),
Some(n) => Ok(n),
}
}
}
impl_unary_ref_ops! {
impl TryNeg::try_neg for $ty;
}
impl_binary_ref_ops! {
impl TryAdd::try_add for $ty;
impl TryDiv::try_div for $ty;
impl TryMul::try_mul for $ty;
impl TryRem::try_rem for $ty;
impl TrySub::try_sub for $ty;
}
)* };
}
impl_uint_ops!(u8, u16, u32, u64, u128, usize);
#[test]
fn test_try_add() {
assert_eq!(i8::try_add(100, 27), Ok(127));
assert_eq!(i8::try_add(100, 28), Err(RangeError::Overflow));
assert_eq!(i8::try_add(-100, -28), Ok(-128));
assert_eq!(i8::try_add(-100, -29), Err(RangeError::Underflow));
assert_eq!(u8::try_add(200, 55), Ok(255));
assert_eq!(u8::try_add(200, 56), Err(Overflow));
}
#[test]
fn test_try_div() {
assert_eq!(i8::try_div(100, 10), Ok(10));
assert_eq!(i8::try_div(100, 0), Err(ArithmeticError::Undefined));
assert_eq!(i8::try_div(-128, -1), Err(ArithmeticError::Overflow));
assert_eq!(u8::try_div(100, 10), Ok(10));
assert_eq!(u8::try_div(100, 0), Err(Undefined));
}
#[test]
fn test_try_mul() {
assert_eq!(i8::try_mul(15, 8), Ok(120));
assert_eq!(i8::try_mul(16, 8), Err(RangeError::Overflow));
assert_eq!(i8::try_mul(16, -8), Ok(-128));
assert_eq!(i8::try_mul(43, -3), Err(RangeError::Underflow));
assert_eq!(i8::try_mul(-127, -1), Ok(127));
assert_eq!(i8::try_mul(-128, -1), Err(RangeError::Overflow));
assert_eq!(u8::try_mul(85, 3), Ok(255));
assert_eq!(u8::try_mul(16, 16), Err(Overflow));
}
#[test]
fn test_try_neg() {
assert_eq!(i8::try_neg(127), Ok(-127));
assert_eq!(i8::try_neg(-128), Err(Overflow));
assert_eq!(u8::try_neg(0), Ok(0));
assert_eq!(u8::try_neg(1), Err(Underflow));
}
#[test]
fn test_try_rem() {
assert_eq!(i8::try_rem(99, 10), Ok(9));
assert_eq!(i8::try_rem(99, -10), Ok(9));
assert_eq!(i8::try_rem(-99, 10), Ok(-9));
assert_eq!(i8::try_rem(-99, -10), Ok(-9));
assert_eq!(i8::try_rem(-128, -1), Ok(0)); assert_eq!(i8::try_rem(99, 0), Err(Undefined));
assert_eq!(u8::try_rem(99, 10), Ok(9));
assert_eq!(u8::try_rem(99, 0), Err(Undefined));
}
#[test]
fn test_try_sub() {
assert_eq!(i8::try_sub(0, -127), Ok(127));
assert_eq!(i8::try_sub(0, -128), Err(RangeError::Overflow));
assert_eq!(i8::try_sub(-1, 127), Ok(-128));
assert_eq!(i8::try_sub(-2, 127), Err(RangeError::Underflow));
assert_eq!(u8::try_sub(100, 100), Ok(0));
assert_eq!(u8::try_sub(0, 1), Err(Underflow));
}