safe_arithmetic/ops/
checked_sub.rs

1use crate::error::{Overflow, Underflow};
2use std::fmt::{self, Debug, Display};
3
4pub trait CheckedSub<Rhs = Self>
5where
6    Self: Sized,
7{
8    type Output;
9    type Error;
10
11    /// Checked arithmetic subtraction from self
12    ///
13    /// # Errors
14    /// When the result of the subtraction can not be represented (e.g. due to an overflow).
15    fn checked_sub(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
16}
17
18macro_rules! impl_unsigned_checked_sub {
19    ( $T:ty ) => {
20        impl CheckedSub for $T {
21            type Output = Self;
22            type Error = SubError<Self, Self>;
23
24            fn checked_sub(self, rhs: Self) -> Result<Self::Output, Self::Error> {
25                num::CheckedSub::checked_sub(&self, &rhs)
26                    .ok_or(rhs.underflows(self))
27                    .map_err(SubError)
28            }
29        }
30    };
31}
32
33impl_unsigned_checked_sub!(u32);
34
35macro_rules! impl_signed_checked_sub {
36    ( $T:ty ) => {
37        impl CheckedSub for $T {
38            type Output = Self;
39            type Error = SubError<Self, Self>;
40
41            fn checked_sub(self, rhs: Self) -> Result<Self::Output, Self::Error> {
42                if rhs.is_negative() {
43                    num::CheckedAdd::checked_add(&self, &rhs.abs())
44                        .ok_or(rhs.overflows(self))
45                        .map_err(SubError)
46                } else {
47                    num::CheckedSub::checked_sub(&self, &rhs)
48                        .ok_or(rhs.underflows(self))
49                        .map_err(SubError)
50                }
51            }
52        }
53    };
54}
55
56impl_signed_checked_sub!(i64);
57
58#[derive(PartialEq, Clone, Debug)]
59#[allow(clippy::module_name_repetitions)]
60pub struct SubError<Lhs, Rhs>(pub crate::error::Operation<Lhs, Rhs>);
61
62impl<Lhs, Rhs> crate::error::Arithmetic for SubError<Lhs, Rhs>
63where
64    Lhs: crate::Type,
65    Rhs: crate::Type,
66{
67}
68
69impl<Lhs, Rhs> std::error::Error for SubError<Lhs, Rhs>
70where
71    Lhs: Display + Debug,
72    Rhs: Display + Debug,
73{
74    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
75        self.0.cause.as_deref().map(crate::error::AsErr::as_err)
76    }
77}
78
79impl<Lhs, Rhs> Display for SubError<Lhs, Rhs>
80where
81    Lhs: Display,
82    Rhs: Display,
83{
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        match self.0.kind {
86            Some(kind) => write!(
87                f,
88                "subtracting {} from {} would {} {}",
89                self.0.rhs,
90                self.0.lhs,
91                kind,
92                std::any::type_name::<Lhs>(),
93            ),
94            None => write!(f, "cannot subtract {} from {}", self.0.rhs, self.0.lhs),
95        }
96    }
97}