1use crate::error::{ArithmeticError, Overflow, RangeError, Undefined, Underflow};
10
11pub trait TryAdd<Rhs = Self> {
13 type Output;
14 type Error;
15
16 fn try_add(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
17}
18
19pub trait TryDiv<Rhs = Self> {
21 type Output;
22 type Error;
23
24 fn try_div(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
25}
26
27pub trait TryMul<Rhs = Self> {
29 type Output;
30 type Error;
31
32 fn try_mul(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
33}
34
35pub trait TryNeg {
37 type Output;
38 type Error;
39
40 fn try_neg(self) -> Result<Self::Output, Self::Error>;
41}
42
43pub trait TryRem<Rhs = Self> {
45 type Output;
46 type Error;
47
48 fn try_rem(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
49}
50
51pub trait TrySub<Rhs = Self> {
53 type Output;
54 type Error;
55
56 fn try_sub(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
57}
58
59macro_rules! impl_unary_ref_ops {
63 { $(impl $trait:ident::$fn:ident for $ty:ident;)* } => { $(
64 impl<'a> $trait for &'a $ty {
65 type Output = $ty;
66 type Error = <$ty as $trait>::Error;
67
68 fn $fn(self) -> Result<$ty, Self::Error> {
69 $trait::$fn(*self)
70 }
71 }
72 )* };
73}
74
75macro_rules! impl_binary_ref_ops {
77 { $(impl $trait:ident::$fn:ident for $ty:ident;)* } => { $(
78 impl<'a> $trait<$ty> for &'a $ty {
79 type Output = $ty;
80 type Error = <$ty as $trait>::Error;
81
82 fn $fn(self, rhs: $ty) -> Result<$ty, Self::Error> {
83 $trait::$fn(*self, rhs)
84 }
85 }
86
87 impl<'r> $trait<&'r $ty> for $ty {
88 type Output = $ty;
89 type Error = <$ty as $trait>::Error;
90
91 fn $fn(self, rhs: &'r $ty) -> Result<$ty, Self::Error> {
92 $trait::$fn(self, *rhs)
93 }
94 }
95
96 impl<'a, 'r> $trait<&'r $ty> for &'a $ty {
97 type Output = $ty;
98 type Error = <$ty as $trait>::Error;
99
100 fn $fn(self, rhs: &'r $ty) -> Result<$ty, Self::Error> {
101 $trait::$fn(*self, *rhs)
102 }
103 }
104 )* };
105}
106
107macro_rules! impl_int_ops {
109 ($($ty:ident),*) => { $(
110 impl TryAdd for $ty {
111 type Output = $ty;
112 type Error = RangeError;
113
114 fn try_add(self, rhs: $ty) -> Result<$ty, RangeError> {
115 match self.checked_add(rhs) {
116 None => Err(if self >= 0 {
117 RangeError::Overflow
118 } else {
119 RangeError::Underflow
120 }),
121 Some(n) => Ok(n),
122 }
123 }
124 }
125
126 impl TryDiv for $ty {
127 type Output = $ty;
128 type Error = ArithmeticError;
129
130 fn try_div(self, rhs: $ty) -> Result<$ty, ArithmeticError> {
131 match self.checked_div(rhs) {
132 None => Err(if rhs == 0 {
133 ArithmeticError::Undefined
134 } else {
135 ArithmeticError::Overflow
137 }),
138 Some(n) => Ok(n),
139 }
140 }
141 }
142
143 impl TryMul for $ty {
144 type Output = $ty;
145 type Error = RangeError;
146
147 fn try_mul(self, rhs: $ty) -> Result<$ty, RangeError> {
148 match self.checked_mul(rhs) {
149 None => Err(if (self >= 0) == (rhs >= 0) {
150 RangeError::Overflow
151 } else {
152 RangeError::Underflow
153 }),
154 Some(n) => Ok(n),
155 }
156 }
157 }
158
159 impl TryNeg for $ty {
160 type Output = $ty;
161 type Error = Overflow;
162
163 fn try_neg(self) -> Result<$ty, Overflow> {
164 match self.checked_neg() {
165 None => Err(Overflow),
166 Some(n) => Ok(n),
167 }
168 }
169 }
170
171 impl TryRem for $ty {
172 type Output = $ty;
173 type Error = Undefined;
174
175 fn try_rem(self, rhs: $ty) -> Result<$ty, Undefined> {
176 match self.checked_rem(rhs) {
177 None => if rhs == 0 {
178 Err(Undefined)
179 } else {
180 Ok(0)
183 },
184 Some(n) => Ok(n),
185 }
186 }
187 }
188
189 impl TrySub for $ty {
190 type Output = $ty;
191 type Error = RangeError;
192
193 fn try_sub(self, rhs: $ty) -> Result<$ty, RangeError> {
194 match self.checked_sub(rhs) {
195 None => Err(if self >= 0 {
196 RangeError::Overflow
197 } else {
198 RangeError::Underflow
199 }),
200 Some(n) => Ok(n),
201 }
202 }
203 }
204
205 impl_unary_ref_ops! {
206 impl TryNeg::try_neg for $ty;
207 }
208
209 impl_binary_ref_ops! {
210 impl TryAdd::try_add for $ty;
211 impl TryDiv::try_div for $ty;
212 impl TryMul::try_mul for $ty;
213 impl TryRem::try_rem for $ty;
214 impl TrySub::try_sub for $ty;
215 }
216 )* };
217}
218
219impl_int_ops!(i8, i16, i32, i64, i128, isize);
220
221macro_rules! impl_uint_ops {
223 ($($ty:ident),*) => { $(
224 impl TryAdd for $ty {
225 type Output = $ty;
226 type Error = Overflow;
227
228 fn try_add(self, rhs: $ty) -> Result<$ty, Overflow> {
229 match self.checked_add(rhs) {
230 None => Err(Overflow),
231 Some(n) => Ok(n),
232 }
233 }
234 }
235
236 impl TryDiv for $ty {
237 type Output = $ty;
238 type Error = Undefined;
239
240 fn try_div(self, rhs: $ty) -> Result<$ty, Undefined> {
241 match self.checked_div(rhs) {
242 None => Err(Undefined),
243 Some(n) => Ok(n),
244 }
245 }
246 }
247
248 impl TryMul for $ty {
249 type Output = $ty;
250 type Error = Overflow;
251
252 fn try_mul(self, rhs: $ty) -> Result<$ty, Overflow> {
253 match self.checked_mul(rhs) {
254 None => Err(Overflow),
255 Some(n) => Ok(n),
256 }
257 }
258 }
259
260 impl TryNeg for $ty {
261 type Output = $ty;
262 type Error = Underflow;
263
264 fn try_neg(self) -> Result<$ty, Underflow> {
265 match self.checked_neg() {
266 None => Err(Underflow),
267 Some(n) => Ok(n),
268 }
269 }
270 }
271
272 impl TryRem for $ty {
273 type Output = $ty;
274 type Error = Undefined;
275
276 fn try_rem(self, rhs: $ty) -> Result<$ty, Undefined> {
277 match self.checked_rem(rhs) {
278 None => Err(Undefined),
279 Some(n) => Ok(n),
280 }
281 }
282 }
283
284 impl TrySub for $ty {
285 type Output = $ty;
286 type Error = Underflow;
287
288 fn try_sub(self, rhs: $ty) -> Result<$ty, Underflow> {
289 match self.checked_sub(rhs) {
290 None => Err(Underflow),
291 Some(n) => Ok(n),
292 }
293 }
294 }
295
296 impl_unary_ref_ops! {
297 impl TryNeg::try_neg for $ty;
298 }
299
300 impl_binary_ref_ops! {
301 impl TryAdd::try_add for $ty;
302 impl TryDiv::try_div for $ty;
303 impl TryMul::try_mul for $ty;
304 impl TryRem::try_rem for $ty;
305 impl TrySub::try_sub for $ty;
306 }
307 )* };
308}
309
310impl_uint_ops!(u8, u16, u32, u64, u128, usize);
311
312#[test]
315fn test_try_add() {
316 assert_eq!(i8::try_add(100, 27), Ok(127));
317 assert_eq!(i8::try_add(100, 28), Err(RangeError::Overflow));
318 assert_eq!(i8::try_add(-100, -28), Ok(-128));
319 assert_eq!(i8::try_add(-100, -29), Err(RangeError::Underflow));
320 assert_eq!(u8::try_add(200, 55), Ok(255));
321 assert_eq!(u8::try_add(200, 56), Err(Overflow));
322}
323
324#[test]
325fn test_try_div() {
326 assert_eq!(i8::try_div(100, 10), Ok(10));
327 assert_eq!(i8::try_div(100, 0), Err(ArithmeticError::Undefined));
328 assert_eq!(i8::try_div(-128, -1), Err(ArithmeticError::Overflow));
329 assert_eq!(u8::try_div(100, 10), Ok(10));
330 assert_eq!(u8::try_div(100, 0), Err(Undefined));
331}
332
333#[test]
334fn test_try_mul() {
335 assert_eq!(i8::try_mul(15, 8), Ok(120));
336 assert_eq!(i8::try_mul(16, 8), Err(RangeError::Overflow));
337 assert_eq!(i8::try_mul(16, -8), Ok(-128));
338 assert_eq!(i8::try_mul(43, -3), Err(RangeError::Underflow));
339 assert_eq!(i8::try_mul(-127, -1), Ok(127));
340 assert_eq!(i8::try_mul(-128, -1), Err(RangeError::Overflow));
341 assert_eq!(u8::try_mul(85, 3), Ok(255));
342 assert_eq!(u8::try_mul(16, 16), Err(Overflow));
343}
344
345#[test]
346fn test_try_neg() {
347 assert_eq!(i8::try_neg(127), Ok(-127));
348 assert_eq!(i8::try_neg(-128), Err(Overflow));
349 assert_eq!(u8::try_neg(0), Ok(0));
350 assert_eq!(u8::try_neg(1), Err(Underflow));
351}
352
353#[test]
354fn test_try_rem() {
355 assert_eq!(i8::try_rem(99, 10), Ok(9));
356 assert_eq!(i8::try_rem(99, -10), Ok(9));
357 assert_eq!(i8::try_rem(-99, 10), Ok(-9));
358 assert_eq!(i8::try_rem(-99, -10), Ok(-9));
359 assert_eq!(i8::try_rem(-128, -1), Ok(0)); assert_eq!(i8::try_rem(99, 0), Err(Undefined));
361 assert_eq!(u8::try_rem(99, 10), Ok(9));
362 assert_eq!(u8::try_rem(99, 0), Err(Undefined));
363}
364
365#[test]
366fn test_try_sub() {
367 assert_eq!(i8::try_sub(0, -127), Ok(127));
368 assert_eq!(i8::try_sub(0, -128), Err(RangeError::Overflow));
369 assert_eq!(i8::try_sub(-1, 127), Ok(-128));
370 assert_eq!(i8::try_sub(-2, 127), Err(RangeError::Underflow));
371 assert_eq!(u8::try_sub(100, 100), Ok(0));
372 assert_eq!(u8::try_sub(0, 1), Err(Underflow));
373}