safe_arithmetic/
error.rs

1use std::any::Any;
2use std::fmt::{self, Debug, Display};
3
4pub trait AsErr {
5    fn as_err(&self) -> &(dyn std::error::Error + 'static);
6}
7
8impl AsErr for dyn std::error::Error + 'static {
9    fn as_err(&self) -> &(dyn std::error::Error + 'static) {
10        self
11    }
12}
13
14impl AsErr for dyn std::error::Error + Send + 'static {
15    fn as_err(&self) -> &(dyn std::error::Error + 'static) {
16        self
17    }
18}
19
20impl AsErr for dyn std::error::Error + Sync + 'static {
21    fn as_err(&self) -> &(dyn std::error::Error + 'static) {
22        self
23    }
24}
25
26impl AsErr for dyn std::error::Error + Send + Sync + 'static {
27    fn as_err(&self) -> &(dyn std::error::Error + 'static) {
28        self
29    }
30}
31
32impl<T> AsErr for T
33where
34    T: std::error::Error + 'static,
35{
36    fn as_err(&self) -> &(dyn std::error::Error + 'static) {
37        self
38    }
39}
40
41pub trait Arithmetic:
42    Sized + Clone + PartialEq + AsErr + std::error::Error + Send + Sync + 'static
43{
44}
45
46pub trait DynArithmetic: AsErr + std::error::Error + Send + Sync + 'static {
47    fn as_any(&self) -> &dyn Any;
48    fn eq(&self, other: &dyn DynArithmetic) -> bool;
49    fn clone(&self) -> Box<dyn DynArithmetic + Send + Sync + 'static>;
50}
51
52impl<E> DynArithmetic for E
53where
54    Self: Arithmetic,
55    E: Send + Sync,
56{
57    fn as_any(&self) -> &dyn Any {
58        self
59    }
60
61    fn eq(&self, other: &dyn DynArithmetic) -> bool {
62        match other.as_any().downcast_ref::<Self>() {
63            Some(other) => PartialEq::eq(self, other),
64            None => false,
65        }
66    }
67
68    fn clone(&self) -> Box<dyn DynArithmetic + Send + Sync + 'static> {
69        Box::new(self.clone())
70    }
71}
72
73impl PartialEq for dyn DynArithmetic + Send + Sync + 'static {
74    fn eq(&self, other: &Self) -> bool {
75        DynArithmetic::eq(self, other)
76    }
77}
78
79// required fix for derived PartialEq that otherwise moves
80impl PartialEq<&Self> for Box<dyn DynArithmetic + Send + Sync + 'static> {
81    fn eq(&self, other: &&Self) -> bool {
82        DynArithmetic::eq(self.as_ref(), other.as_ref())
83    }
84}
85
86impl Clone for Box<dyn DynArithmetic + Send + Sync + 'static> {
87    fn clone(&self) -> Box<dyn DynArithmetic + Send + Sync + 'static> {
88        // let source: &dyn DynArithmetic = self.as_ref();
89        self.as_ref().clone()
90    }
91}
92
93#[derive(PartialEq, Clone, Debug)]
94pub struct Error(pub Box<dyn DynArithmetic + Sync + Send + 'static>);
95
96impl std::ops::Deref for Error {
97    type Target = dyn DynArithmetic + Sync + Send + 'static;
98
99    fn deref(&self) -> &Self::Target {
100        &*self.0
101    }
102}
103
104impl Display for Error {
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        Display::fmt(&self.0, f)
107    }
108}
109
110impl std::error::Error for Error {
111    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
112        self.0.source()
113    }
114}
115
116impl<E> From<E> for Error
117where
118    E: Arithmetic,
119{
120    fn from(err: E) -> Self {
121        Error(Box::new(err))
122    }
123}
124
125#[derive(PartialEq, Eq, Clone, Copy, Debug)]
126pub enum Kind {
127    Overflow,
128    Underflow,
129    DivideByZero,
130}
131
132impl Display for Kind {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        match self {
135            Kind::Underflow => write!(f, "underflow"),
136            Kind::Overflow => write!(f, "overflow"),
137            Kind::DivideByZero => write!(f, "divide by zero"),
138        }
139    }
140}
141
142#[derive(PartialEq, Clone, Debug)]
143pub struct Operation<Lhs, Rhs> {
144    pub lhs: Lhs,
145    pub rhs: Rhs,
146    pub kind: Option<Kind>,
147    pub cause: Option<Error>,
148}
149
150pub trait DivideByZero<Rhs>
151where
152    Self: super::Type + Sized + Copy,
153    Rhs: super::Type + Sized + Copy + num::Zero,
154{
155    fn divide_by_zero(self) -> Operation<Self, Rhs> {
156        Operation {
157            lhs: self,
158            rhs: Rhs::zero(),
159            kind: Some(Kind::DivideByZero),
160            cause: None,
161        }
162    }
163}
164
165pub trait Overflow<Lhs>
166where
167    Self: super::Type + Sized + Copy,
168    Lhs: super::Type + Sized + Copy,
169{
170    fn overflows(self, lhs: Lhs) -> Operation<Lhs, Self> {
171        Operation {
172            lhs,
173            rhs: self,
174            kind: Some(Kind::Overflow),
175            cause: None,
176        }
177    }
178}
179
180pub trait Underflow<Lhs>
181where
182    Self: super::Type + Sized + Copy,
183    Lhs: super::Type + Sized + Copy,
184{
185    fn underflows(self, lhs: Lhs) -> Operation<Lhs, Self> {
186        Operation {
187            lhs,
188            rhs: self,
189            kind: Some(Kind::Underflow),
190            cause: None,
191        }
192    }
193}
194
195impl<L, R> Underflow<L> for R
196where
197    L: super::Type + Sized + Copy,
198    R: super::Type + Sized + Copy,
199{
200}
201
202impl<L, R> Overflow<L> for R
203where
204    L: super::Type + Sized + Copy,
205    R: super::Type + Sized + Copy,
206{
207}
208
209impl<R, L> DivideByZero<R> for L
210where
211    L: super::Type + Sized + Copy,
212    R: super::Type + Sized + Copy + num::Zero,
213{
214}
215
216#[cfg(test)]
217mod tests {
218    use super::DynArithmetic as ArithmeticError;
219    use crate::{error::Overflow, ops};
220
221    #[test]
222    fn arithmetic_error_is_std_error() {
223        let err: &dyn ArithmeticError = &ops::AddError(10u32.overflows(10u32));
224        let _: &dyn std::error::Error = &err;
225    }
226
227    #[test]
228    fn arithmetic_error_partial_eq() {
229        type BoxedError = Box<dyn ArithmeticError + Send + Sync + 'static>;
230        let add_err1: BoxedError = Box::new(ops::AddError(10u32.overflows(10u32)));
231        let add_err2: BoxedError = Box::new(ops::AddError(10u32.overflows(10u64)));
232        assert!(add_err1 == add_err1);
233        assert!(add_err1 != add_err2);
234
235        let sub_err1: BoxedError = Box::new(ops::SubError(10u32.overflows(12u32)));
236        let sub_err2: BoxedError = Box::new(ops::SubError(10u32.overflows(15u32)));
237        assert!(sub_err1 == sub_err1);
238        assert!(sub_err1 != sub_err2);
239    }
240}