safe_arithmetic/ops/
checked_div.rs1use crate::error::{DivideByZero, Overflow, Underflow};
2use num::Zero;
3use std::fmt::{self, Debug, Display};
4
5pub trait CheckedDiv<Rhs = Self>
6where
7 Self: Sized,
8{
9 type Output;
10 type Error;
11
12 fn checked_div(self, scalar: Rhs) -> Result<Self::Output, Self::Error>;
17}
18
19macro_rules! impl_unsigned_checked_div {
20 ( $T:ty ) => {
21 impl CheckedDiv for $T {
22 type Output = Self;
23 type Error = DivError<Self, Self>;
24
25 fn checked_div(self, rhs: Self) -> Result<Self::Output, Self::Error> {
26 if rhs.is_zero() {
28 Err(DivError(self.divide_by_zero()))
29 } else {
30 num::CheckedDiv::checked_div(&self, &rhs)
31 .ok_or(rhs.underflows(self))
32 .map_err(DivError)
33 }
34 }
35 }
36 };
37}
38
39impl_unsigned_checked_div!(u32);
40
41macro_rules! impl_signed_checked_div {
42 ( $T:ty ) => {
43 impl CheckedDiv for $T {
44 type Output = Self;
45 type Error = DivError<Self, Self>;
46
47 fn checked_div(self, rhs: Self) -> Result<Self::Output, Self::Error> {
48 if rhs.is_zero() {
50 Err(DivError(self.divide_by_zero()))
51 } else if self.signum() == rhs.signum() {
52 num::CheckedDiv::checked_div(&self, &rhs)
54 .ok_or(rhs.overflows(self))
55 .map_err(DivError)
56 } else {
57 num::CheckedDiv::checked_div(&self, &rhs)
59 .ok_or(rhs.underflows(self))
60 .map_err(DivError)
61 }
62 }
63 }
64 };
65}
66
67impl_signed_checked_div!(i64);
68
69macro_rules! impl_float_checked_div {
70 ( $T:ty ) => {
71 impl CheckedDiv for $T {
72 type Output = Self;
73 type Error = DivError<Self, Self>;
74
75 fn checked_div(self, rhs: Self) -> Result<Self::Output, Self::Error> {
76 if rhs.is_zero() {
78 return Err(DivError(self.divide_by_zero()));
79 }
80 let result = self / rhs;
81 if result.is_nan() && self.signum() == rhs.signum() {
82 Err(DivError(rhs.overflows(self)))
84 } else if result.is_nan() {
85 Err(DivError(rhs.underflows(self)))
87 } else {
88 Ok(result)
89 }
90 }
91 }
92 };
93}
94
95impl_float_checked_div!(f64);
96
97#[derive(PartialEq, Clone, Debug)]
98#[allow(clippy::module_name_repetitions)]
99pub struct DivError<Lhs, Rhs>(pub crate::error::Operation<Lhs, Rhs>);
100
101impl<Lhs, Rhs> crate::error::Arithmetic for DivError<Lhs, Rhs>
102where
103 Lhs: crate::Type,
104 Rhs: crate::Type,
105{
106}
107
108impl<Lhs, Rhs> std::error::Error for DivError<Lhs, Rhs>
109where
110 Lhs: Display + Debug,
111 Rhs: Display + Debug,
112{
113 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
114 self.0.cause.as_deref().map(crate::error::AsErr::as_err)
115 }
116}
117
118impl<Lhs, Rhs> Display for DivError<Lhs, Rhs>
119where
120 Lhs: Display,
121 Rhs: Display,
122{
123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 match self.0.kind {
125 Some(kind) => match kind {
126 crate::error::Kind::DivideByZero => {
127 write!(f, "dividing {} by {} is undefined", self.0.lhs, self.0.rhs)
128 }
129 other => {
130 write!(
131 f,
132 "dividing {} by {} would {} {}",
133 self.0.lhs,
134 self.0.rhs,
135 other,
136 std::any::type_name::<Lhs>(),
137 )
138 }
139 },
140 None => write!(f, "cannot divide {} by {}", self.0.lhs, self.0.rhs),
141 }
142 }
143}