pub trait SafeSub: Sized {
fn checked_sub(&self, r: &Self) -> Option<Self>;
fn saturating_sub(&self, r: &Self) -> Self;
fn wrapping_sub(&self, r: &Self) -> Self;
}
macro_rules! impl_safe_sub {
($($t:ty),*) => {
$(
impl SafeSub for $t {
fn checked_sub(&self, r: &Self) -> Option<Self> {
<$t>::checked_sub(*self, *r)
}
fn saturating_sub(&self, r: &Self) -> Self {
<$t>::saturating_sub(*self, *r)
}
fn wrapping_sub(&self, r: &Self) -> Self {
<$t>::wrapping_sub(*self, *r)
}
}
)*
};
}
impl_safe_sub!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
use num_bigint::BigInt;
use crate::value::{decimal::Decimal, int::Int, uint::Uint};
impl SafeSub for Int {
fn checked_sub(&self, r: &Self) -> Option<Self> {
Some(Int::from(&self.0 - &r.0))
}
fn saturating_sub(&self, r: &Self) -> Self {
Int::from(&self.0 - &r.0)
}
fn wrapping_sub(&self, r: &Self) -> Self {
Int::from(&self.0 - &r.0)
}
}
impl SafeSub for Uint {
fn checked_sub(&self, r: &Self) -> Option<Self> {
let result = &self.0 - &r.0;
if result < BigInt::from(0) {
None
} else {
Some(Uint::from(result))
}
}
fn saturating_sub(&self, r: &Self) -> Self {
let result = &self.0 - &r.0;
if result < BigInt::from(0) {
Uint::from(0u64)
} else {
Uint::from(result)
}
}
fn wrapping_sub(&self, r: &Self) -> Self {
let result = &self.0 - &r.0;
if result < BigInt::from(0) {
Uint::from(0u64)
} else {
Uint::from(result)
}
}
}
impl SafeSub for Decimal {
fn checked_sub(&self, r: &Self) -> Option<Self> {
let result = self.inner() - r.inner();
Some(Decimal::from(result))
}
fn saturating_sub(&self, r: &Self) -> Self {
let result = self.inner() - r.inner();
Decimal::from(result)
}
fn wrapping_sub(&self, r: &Self) -> Self {
let result = self.inner() - r.inner();
Decimal::from(result)
}
}
impl SafeSub for f32 {
fn checked_sub(&self, r: &Self) -> Option<Self> {
let result = *self - *r;
if result.is_finite() {
Some(result)
} else {
None
}
}
fn saturating_sub(&self, r: &Self) -> Self {
let result = *self - *r;
if result.is_infinite() {
if result.is_sign_negative() {
f32::MIN
} else {
f32::MAX
}
} else {
result
}
}
fn wrapping_sub(&self, r: &Self) -> Self {
*self - *r
}
}
impl SafeSub for f64 {
fn checked_sub(&self, r: &Self) -> Option<Self> {
let result = *self - *r;
if result.is_finite() {
Some(result)
} else {
None
}
}
fn saturating_sub(&self, r: &Self) -> Self {
let result = *self - *r;
if result.is_infinite() {
if result.is_sign_negative() {
f64::MIN
} else {
f64::MAX
}
} else {
result
}
}
fn wrapping_sub(&self, r: &Self) -> Self {
*self - *r
}
}
#[cfg(test)]
pub mod tests {
macro_rules! define_tests {
($($t:ty => $mod:ident),*) => {
$(
mod $mod {
use super::super::SafeSub;
#[test]
fn checked_sub_happy() {
let x: $t = 20;
let y: $t = 10;
assert_eq!(SafeSub::checked_sub(&x, &y), Some(10));
}
#[test]
fn checked_sub_unhappy() {
let x: $t = <$t>::MIN;
let y: $t = 1;
assert_eq!(SafeSub::checked_sub(&x, &y), None);
}
#[test]
fn saturating_sub_happy() {
let x: $t = 20;
let y: $t = 10;
assert_eq!(SafeSub::saturating_sub(&x, &y), 10);
}
#[test]
fn saturating_sub_unhappy() {
let x: $t = <$t>::MIN;
let y: $t = 1;
assert_eq!(SafeSub::saturating_sub(&x, &y), <$t>::MIN);
}
#[test]
fn wrapping_sub_happy() {
let x: $t = 20;
let y: $t = 10;
assert_eq!(SafeSub::wrapping_sub(&x, &y), 10);
}
#[test]
fn wrapping_sub_unhappy() {
let x: $t = <$t>::MIN;
let y: $t = 1;
assert_eq!(SafeSub::wrapping_sub(&x, &y), <$t>::MAX);
}
}
)*
};
}
define_tests!(
i8 => i8_tests,
i16 => i16_tests,
i32 => i32_tests,
i64 => i64_tests,
i128 => i128_tests,
u8 => u8_tests,
u16 => u16_tests,
u32 => u32_tests,
u64 => u64_tests,
u128 => u128_tests
);
}