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
79impl 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 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}