random_stuff/
ops.rs

1/// Adds arithmetic operations similar to `checked_*` but returning Result with nice errors
2pub trait ArithmeticTryOps<RHS = Self> where Self: Sized + core::fmt::Display + core::fmt::Debug + TypeName, RHS: Sized + core::fmt::Display + core::fmt::Debug {
3    fn try_add(self, other: RHS) -> Result<Self, OverflowError<Self, RHS>>;
4    fn try_sub(self, other: RHS) -> Result<Self, OverflowError<Self, RHS>>;
5    fn try_mul(self, other: RHS) -> Result<Self, OverflowError<Self, RHS>>;
6    fn try_div(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
7    fn try_div_euclid(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
8    fn try_rem(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
9    fn try_rem_euclid(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
10    fn try_pow(self, other: u32) -> Result<Self, OverflowError<Self, u32>>;
11    //fn try_next_power_of_two(self) -> Result<Self, NextPowerOfTwoError<Self>;
12    fn try_shl(self, other: u32) -> Result<Self, BigShiftError<Self>>;
13    fn try_shr(self, other: u32) -> Result<Self, BigShiftError<Self>>;
14}
15
16#[derive(Debug, thiserror::Error)]
17#[error("operation {left} {op} {right} overflowed")]
18pub struct OverflowError<L: core::fmt::Display + core::fmt::Debug, R: core::fmt::Display + core::fmt::Debug> {
19    left: L,
20    op: &'static str,
21    right: R,
22}
23
24#[derive(Debug, thiserror::Error)]
25#[error("attempted to divide {0} by zero")]
26pub struct DivisionByZeroError<T: core::fmt::Display + core::fmt::Debug>(T);
27
28/// Retrurned from << and >> when RHS is too much
29#[derive(Debug, thiserror::Error)]
30#[error("operation {left} {op} {right} attempted to shift too much (the type of LHS is {})", L::type_name())]
31pub struct BigShiftError<L: core::fmt::Display + TypeName + core::fmt::Debug> {
32    left: L,
33    op: &'static str,
34    right: u32,
35}
36
37pub trait TypeName {
38    fn type_name() -> &'static str;
39}
40
41macro_rules! impl_type_names {
42    ($($type:ty),*) => {
43        $(
44            impl TypeName for $type {
45                fn type_name() -> &'static str {
46                    stringify!($type)
47                }
48            }
49        )*
50    }
51}
52
53macro_rules! impl_overflowing_op {
54    ($try_op:ident, $check_op:ident, $rhs:ty, $op_str:expr) => {
55        fn $try_op(self, other: $rhs) -> Result<Self, OverflowError<Self, $rhs>> {
56            self.$check_op(other).ok_or(OverflowError {
57                left: self,
58                op: $op_str,
59                right: other,
60            })
61        }
62    }
63}
64
65macro_rules! impl_arith_op {
66    ($($type:ty),*) => {
67        $(
68            impl_type_names!($type);
69
70            impl ArithmeticTryOps for $type {
71                impl_overflowing_op!(try_add, checked_add, $type, "+");
72                impl_overflowing_op!(try_sub, checked_sub, $type, "-");
73                impl_overflowing_op!(try_mul, checked_mul, $type, "*");
74                // we don't use ^ to avoid mistaking it for bit xor
75                impl_overflowing_op!(try_pow, checked_pow, u32, "**");
76
77                fn try_div(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
78                    self.checked_div(other).ok_or(DivisionByZeroError(self))
79                }
80
81                fn try_div_euclid(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
82                    self.checked_div_euclid(other).ok_or(DivisionByZeroError(self))
83                }
84
85                fn try_rem(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
86                    self.checked_rem(other).ok_or(DivisionByZeroError(self))
87                }
88
89                fn try_rem_euclid(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
90                    self.checked_rem_euclid(other).ok_or(DivisionByZeroError(self))
91                }
92
93                fn try_shl(self, other: u32) -> Result<Self, BigShiftError<Self>> {
94                    self.checked_shl(other).ok_or(BigShiftError {
95                        left: self,
96                        op: "<<",
97                        right: other,
98                    })
99                }
100
101                fn try_shr(self, other: u32) -> Result<Self, BigShiftError<Self>> {
102                    self.checked_shr(other).ok_or(BigShiftError {
103                        left: self,
104                        op: ">>",
105                        right: other,
106                    })
107                }
108            }
109        )*
110    }
111}
112
113impl_arith_op!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
114
115#[cfg(test)]
116mod tests {
117    use super::ArithmeticTryOps;
118
119    #[test]
120    fn add() {
121        assert!(255u8.try_add(1).is_err());
122    }
123}