Remove manual map implementation in maybe math (#161)
1//! Contains numerical helper traits and functions
2
3/// A trait to conveniently calculate minimums and maximums when some data may not be defined
4///
5/// If the left-hand value is [`None`], these operations return [`None`].
6/// If the right-hand value is [`None`], it is treated as zero.
7pub(crate) trait MaybeMath<In, Out> {
8 /// Returns the minimum of `self` and `rhs`
9 fn maybe_min(self, rhs: In) -> Out;
10
11 /// Returns the maximum of `self` and `rhs`
12 fn maybe_max(self, rhs: In) -> Out;
13
14 /// Adds `self` and `rhs`.
15 fn maybe_add(self, rhs: In) -> Out;
16
17 /// Subtracts rhs from `self`, treating [`None`] values as default
18 fn maybe_sub(self, rhs: In) -> Out;
19}
20
21impl MaybeMath<Option<f32>, Option<f32>> for Option<f32> {
22 fn maybe_min(self, rhs: Option<f32>) -> Option<f32> {
23 match (self, rhs) {
24 (Some(l), Some(r)) => Some(l.min(r)),
25 (Some(_l), None) => self,
26 (None, Some(_r)) => None,
27 (None, None) => None,
28 }
29 }
30
31 fn maybe_max(self, rhs: Option<f32>) -> Option<f32> {
32 match (self, rhs) {
33 (Some(l), Some(r)) => Some(l.max(r)),
34 (Some(_l), None) => self,
35 (None, Some(_r)) => None,
36 (None, None) => None,
37 }
38 }
39
40 fn maybe_add(self, rhs: Option<f32>) -> Option<f32> {
41 match (self, rhs) {
42 (Some(l), Some(r)) => Some(l + r),
43 (Some(_l), None) => self,
44 (None, Some(_r)) => None,
45 (None, None) => None,
46 }
47 }
48
49 fn maybe_sub(self, rhs: Option<f32>) -> Option<f32> {
50 match (self, rhs) {
51 (Some(l), Some(r)) => Some(l - r),
52 (Some(_l), None) => self,
53 (None, Some(_r)) => None,
54 (None, None) => None,
55 }
56 }
57}
58
59impl MaybeMath<f32, Option<f32>> for Option<f32> {
60 fn maybe_min(self, rhs: f32) -> Option<f32> {
61 self.map(|val| val.min(rhs))
62 }
63
64 fn maybe_max(self, rhs: f32) -> Option<f32> {
65 self.map(|val| val.max(rhs))
66 }
67
68 fn maybe_add(self, rhs: f32) -> Option<f32> {
69 self.map(|val| val + rhs)
70 }
71
72 fn maybe_sub(self, rhs: f32) -> Option<f32> {
73 self.map(|val| val - rhs)
74 }
75}
76
77impl MaybeMath<Option<f32>, f32> for f32 {
78 fn maybe_min(self, rhs: Option<f32>) -> f32 {
79 match rhs {
80 Some(val) => self.min(val),
81 None => self,
82 }
83 }
84
85 fn maybe_max(self, rhs: Option<f32>) -> f32 {
86 match rhs {
87 Some(val) => self.max(val),
88 None => self,
89 }
90 }
91
92 fn maybe_add(self, rhs: Option<f32>) -> f32 {
93 match rhs {
94 Some(val) => self + val,
95 None => self,
96 }
97 }
98
99 fn maybe_sub(self, rhs: Option<f32>) -> f32 {
100 match rhs {
101 Some(val) => self - val,
102 None => self,
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 mod lhs_option_f32_rhs_option_f32 {
110
111 use crate::math::MaybeMath;
112 use rstest::rstest;
113
114 #[rstest]
115 #[case(Some(3.0), Some(5.0), Some(3.0))]
116 #[case(Some(5.0), Some(3.0), Some(3.0))]
117 #[case(Some(3.0), None, Some(3.0))]
118 #[case(None, Some(3.0), None)]
119 #[case(None, None, None)]
120 fn test_maybe_min(#[case] lhs: Option<f32>, #[case] rhs: Option<f32>, #[case] expected: Option<f32>) {
121 assert_eq!(lhs.maybe_min(rhs), expected);
122 }
123
124 #[rstest]
125 #[case(Some(3.0), Some(5.0), Some(5.0))]
126 #[case(Some(5.0), Some(3.0), Some(5.0))]
127 #[case(Some(3.0), None, Some(3.0))]
128 #[case(None, Some(3.0), None)]
129 #[case(None, None, None)]
130 fn test_maybe_max(#[case] lhs: Option<f32>, #[case] rhs: Option<f32>, #[case] expected: Option<f32>) {
131 assert_eq!(lhs.maybe_max(rhs), expected);
132 }
133
134 #[rstest]
135 #[case(Some(3.0), Some(5.0), Some(8.0))]
136 #[case(Some(5.0), Some(3.0), Some(8.0))]
137 #[case(Some(3.0), None, Some(3.0))]
138 #[case(None, Some(3.0), None)]
139 #[case(None, None, None)]
140 fn test_maybe_add(#[case] lhs: Option<f32>, #[case] rhs: Option<f32>, #[case] expected: Option<f32>) {
141 assert_eq!(lhs.maybe_add(rhs), expected);
142 }
143
144 #[rstest]
145 #[case(Some(3.0), Some(5.0), Some(-2.0))]
146 #[case(Some(5.0), Some(3.0), Some(2.0))]
147 #[case(Some(3.0), None, Some(3.0))]
148 #[case(None, Some(3.0), None)]
149 #[case(None, None, None)]
150 fn test_maybe_sub(#[case] lhs: Option<f32>, #[case] rhs: Option<f32>, #[case] expected: Option<f32>) {
151 assert_eq!(lhs.maybe_sub(rhs), expected);
152 }
153 }
154
155 mod lhs_option_f32_rhs_f32 {
156
157 use crate::math::MaybeMath;
158 use rstest::rstest;
159
160 #[rstest]
161 #[case(Some(3.0), 5.0, Some(3.0))]
162 #[case(Some(5.0), 3.0, Some(3.0))]
163 #[case(None, 3.0, None)]
164 fn test_maybe_min(#[case] lhs: Option<f32>, #[case] rhs: f32, #[case] expected: Option<f32>) {
165 assert_eq!(lhs.maybe_min(rhs), expected);
166 }
167
168 #[rstest]
169 #[case(Some(3.0), 5.0, Some(5.0))]
170 #[case(Some(5.0), 3.0, Some(5.0))]
171 #[case(None, 3.0, None)]
172 fn test_maybe_max(#[case] lhs: Option<f32>, #[case] rhs: f32, #[case] expected: Option<f32>) {
173 assert_eq!(lhs.maybe_max(rhs), expected);
174 }
175
176 #[rstest]
177 #[case(Some(3.0), 5.0, Some(8.0))]
178 #[case(Some(5.0), 3.0, Some(8.0))]
179 #[case(None, 3.0, None)]
180 fn test_maybe_add(#[case] lhs: Option<f32>, #[case] rhs: f32, #[case] expected: Option<f32>) {
181 assert_eq!(lhs.maybe_add(rhs), expected);
182 }
183
184 #[rstest]
185 #[case(Some(3.0), 5.0, Some(-2.0))]
186 #[case(Some(5.0), 3.0, Some(2.0))]
187 #[case(None, 3.0, None)]
188 fn test_maybe_sub(#[case] lhs: Option<f32>, #[case] rhs: f32, #[case] expected: Option<f32>) {
189 assert_eq!(lhs.maybe_sub(rhs), expected);
190 }
191 }
192
193 mod lhs_f32_rhs_option_f32 {
194
195 use crate::math::MaybeMath;
196 use rstest::rstest;
197
198 #[rstest]
199 #[case(3.0, Some(5.0), 3.0)]
200 #[case(5.0, Some(3.0), 3.0)]
201 #[case(3.0, None, 3.0)]
202 fn test_maybe_min(#[case] lhs: f32, #[case] rhs: Option<f32>, #[case] expected: f32) {
203 assert_eq!(lhs.maybe_min(rhs), expected);
204 }
205
206 #[rstest]
207 #[case(3.0, Some(5.0), 5.0)]
208 #[case(5.0, Some(3.0), 5.0)]
209 #[case(3.0, None, 3.0)]
210 fn test_maybe_max(#[case] lhs: f32, #[case] rhs: Option<f32>, #[case] expected: f32) {
211 assert_eq!(lhs.maybe_max(rhs), expected);
212 }
213
214 #[rstest]
215 #[case(3.0, Some(5.0), 8.0)]
216 #[case(5.0, Some(3.0), 8.0)]
217 #[case(3.0, None, 3.0)]
218 fn test_maybe_add(#[case] lhs: f32, #[case] rhs: Option<f32>, #[case] expected: f32) {
219 assert_eq!(lhs.maybe_add(rhs), expected);
220 }
221
222 #[rstest]
223 #[case(3.0, Some(5.0), -2.0)]
224 #[case(5.0, Some(3.0), 2.0)]
225 #[case(3.0, None, 3.0)]
226 fn test_maybe_sub(#[case] lhs: f32, #[case] rhs: Option<f32>, #[case] expected: f32) {
227 assert_eq!(lhs.maybe_sub(rhs), expected);
228 }
229 }
230}