irox_units/
bounds.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3
4//!
5//! Bounding Boxes and Range Checks
6//!
7
8use core::fmt::{Debug, Display, Formatter};
9
10///
11/// Defines a basic four-corner bounding box
12pub struct Bounds<T> {
13    /// The upper left coordinate
14    upper_left: T,
15    /// The upper right coordinate
16    upper_right: T,
17    /// The lower left coordinate
18    lower_left: T,
19    /// The lower right coordinate
20    lower_right: T,
21}
22
23impl<T> Bounds<T> {
24    ///
25    /// Creates a new bounding box using the specified four corners
26    pub const fn new(upper_left: T, upper_right: T, lower_left: T, lower_right: T) -> Bounds<T> {
27        Bounds {
28            upper_left,
29            upper_right,
30            lower_left,
31            lower_right,
32        }
33    }
34
35    pub fn upper_left_corner(&self) -> &T {
36        &self.upper_left
37    }
38
39    pub fn lower_left_corner(&self) -> &T {
40        &self.lower_left
41    }
42
43    pub fn upper_right_corner(&self) -> &T {
44        &self.upper_right
45    }
46
47    pub fn lower_right_corner(&self) -> &T {
48        &self.lower_right
49    }
50}
51
52///
53/// A trait to check if a particular value is within range
54pub trait Range<T>
55where
56    T: Debug + Display + Clone + PartialOrd,
57{
58    type Error;
59
60    ///
61    /// Returns true if the value is valid for the specified range
62    fn value_is_valid(&self, value: &T) -> bool;
63
64    ///
65    /// Checks if the value is valid, if valid, returns `Ok(())`
66    /// If not valid, returns `Err(Error)`
67    fn check_value_is_valid(&self, value: &T) -> Result<(), Self::Error>;
68}
69
70///
71/// A [`Range`] implementation to verify a value is less than the reference value
72#[derive(Debug, Clone)]
73pub struct LessThanValue<T>
74where
75    T: Debug + Display + Clone + PartialOrd,
76{
77    pub(crate) value: T,
78}
79impl<T: Debug + Display + Clone + PartialOrd> LessThanValue<T> {
80    #[must_use]
81    pub const fn new(value: T) -> Self {
82        Self { value }
83    }
84
85    /// Returns the first invalid value for this range.  All values MUST be less than this value
86    pub fn value(&self) -> &T {
87        &self.value
88    }
89}
90impl<T: Debug + Display + Clone + PartialOrd> Range<T> for LessThanValue<T> {
91    type Error = GreaterThanEqualToValueError<T>;
92    fn value_is_valid(&self, value: &T) -> bool {
93        value.lt(&self.value)
94    }
95
96    fn check_value_is_valid(&self, value: &T) -> Result<(), Self::Error> {
97        if self.value_is_valid(value) {
98            return Ok(());
99        }
100        Err(GreaterThanEqualToValueError {
101            value: value.clone(),
102            valid_range: self.clone(),
103        })
104    }
105}
106
107///
108/// Error type for when a value is less than or equal to the reference value
109#[derive(Debug, Clone)]
110pub struct LessThanEqualToValueError<T>
111where
112    T: Debug + Display + Clone + PartialOrd,
113{
114    pub(crate) value: T,
115    pub(crate) valid_range: GreaterThanValue<T>,
116}
117
118impl<T: Debug + Display + Clone + PartialOrd> Display for LessThanEqualToValueError<T> {
119    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
120        f.write_fmt(format_args!(
121            "value {} is less than or equal to {}",
122            self.value, self.valid_range.value
123        ))
124    }
125}
126
127impl<T: Debug + Display + Clone + PartialOrd> LessThanEqualToValueError<T> {
128    /// Returns the offending value that is out of range
129    pub fn value(&self) -> &T {
130        &self.value
131    }
132
133    /// Returns the range of valid values this value breaks
134    pub fn valid_range(&self) -> &GreaterThanValue<T> {
135        &self.valid_range
136    }
137}
138#[cfg(feature = "std")]
139impl<T: Debug + Display + Clone + PartialOrd> std::error::Error for LessThanEqualToValueError<T> {}
140
141///
142/// A [`Range`] implementation to verify a value is greater than the reference value
143#[derive(Debug, Clone)]
144pub struct GreaterThanValue<T>
145where
146    T: Debug + Display + Clone + PartialOrd,
147{
148    pub(crate) value: T,
149}
150
151impl<T: Debug + Display + Clone + PartialOrd> GreaterThanValue<T> {
152    #[must_use]
153    pub const fn new(value: T) -> Self {
154        Self { value }
155    }
156
157    /// Returns the first smallest invalid value for the range.  All values MUST be greater than
158    /// this value.
159    pub fn value(&self) -> &T {
160        &self.value
161    }
162}
163
164impl<T: Debug + Display + Clone + PartialOrd> Range<T> for GreaterThanValue<T> {
165    type Error = LessThanEqualToValueError<T>;
166
167    fn value_is_valid(&self, value: &T) -> bool {
168        value.gt(&self.value)
169    }
170
171    fn check_value_is_valid(&self, value: &T) -> Result<(), Self::Error> {
172        if self.value_is_valid(value) {
173            return Ok(());
174        }
175        Err(LessThanEqualToValueError {
176            valid_range: self.clone(),
177            value: value.clone(),
178        })
179    }
180}
181
182///
183/// An error type to indicate that the checked value is greater than or equal to
184/// the valid reference value
185#[derive(Debug, Clone)]
186pub struct GreaterThanEqualToValueError<T>
187where
188    T: Debug + Display + Clone + PartialOrd,
189{
190    pub(crate) value: T,
191    pub(crate) valid_range: LessThanValue<T>,
192}
193impl<T: Debug + Display + Clone + PartialOrd> Display for GreaterThanEqualToValueError<T> {
194    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
195        f.write_fmt(format_args!(
196            "the value {} is greater than or equal to {}",
197            self.value, self.valid_range.value
198        ))
199    }
200}
201#[cfg(feature = "std")]
202impl<T: Debug + Display + Clone + PartialOrd> std::error::Error
203    for GreaterThanEqualToValueError<T>
204{
205}
206
207impl<T: Debug + Display + Clone + PartialOrd> GreaterThanEqualToValueError<T> {
208    #[must_use]
209    pub fn new(value: T, valid_range: LessThanValue<T>) -> Self {
210        Self { value, valid_range }
211    }
212
213    pub fn err<O>(value: T, valid_range: LessThanValue<T>) -> Result<O, Self> {
214        Err(Self::new(value, valid_range))
215    }
216
217    /// Returns the value that is out of range.
218    pub fn value(&self) -> &T {
219        &self.value
220    }
221
222    /// Returns the valid range that the value is outside of.
223    pub fn valid_range(&self) -> &LessThanValue<T> {
224        &self.valid_range
225    }
226}
227macro_rules! upconvert_error_type {
228    ($lower:tt,$upper:tt,$error:tt) => {
229        impl From<$error<$lower>> for $error<$upper> {
230            fn from(value: $error<$lower>) -> Self {
231                $error {
232                    value: value.value as $upper,
233                    valid_range: LessThanValue {
234                        value: value.valid_range.value as $upper,
235                    },
236                }
237            }
238        }
239    };
240}
241upconvert_error_type!(u8, u16, GreaterThanEqualToValueError);
242upconvert_error_type!(u8, u32, GreaterThanEqualToValueError);
243upconvert_error_type!(u8, u64, GreaterThanEqualToValueError);
244upconvert_error_type!(u8, u128, GreaterThanEqualToValueError);
245upconvert_error_type!(u8, f32, GreaterThanEqualToValueError);
246upconvert_error_type!(u8, f64, GreaterThanEqualToValueError);
247upconvert_error_type!(u16, u32, GreaterThanEqualToValueError);
248upconvert_error_type!(u16, u64, GreaterThanEqualToValueError);
249upconvert_error_type!(u16, u128, GreaterThanEqualToValueError);
250upconvert_error_type!(u16, f32, GreaterThanEqualToValueError);
251upconvert_error_type!(u16, f64, GreaterThanEqualToValueError);
252upconvert_error_type!(u32, u64, GreaterThanEqualToValueError);
253upconvert_error_type!(u32, u128, GreaterThanEqualToValueError);
254upconvert_error_type!(u32, f32, GreaterThanEqualToValueError);
255upconvert_error_type!(u32, f64, GreaterThanEqualToValueError);
256
257///
258/// A [`Range`] implementation to verify a value is between two reference values
259#[derive(Debug, Clone)]
260pub struct WithinRange<T>
261where
262    T: Debug + Display + Clone + PartialOrd,
263{
264    pub(crate) lower_bound: GreaterThanValue<T>,
265    pub(crate) upper_bound: LessThanValue<T>,
266}
267impl<T: Debug + Display + Clone + PartialOrd> WithinRange<T> {
268    /// Creates a new [`WithinRange`], which is implemented as the union of a [`LessThanValue<T>`]
269    /// and a [`GreaterThanValue<T>`].
270    ///
271    /// Somewhat confusingly, `lower_bound` and `upper_bound` are the first two INVALID values
272    /// bounding this range.
273    #[must_use]
274    pub const fn new(lower_bound: T, upper_bound: T) -> Self {
275        Self {
276            lower_bound: GreaterThanValue::new(lower_bound),
277            upper_bound: LessThanValue::new(upper_bound),
278        }
279    }
280
281    /// Returns the lower bound of this range
282    #[must_use]
283    pub fn lower_bound(&self) -> &GreaterThanValue<T> {
284        &self.lower_bound
285    }
286    /// Returns the upper bound of this range
287    #[must_use]
288    pub fn upper_bound(&self) -> &LessThanValue<T> {
289        &self.upper_bound
290    }
291}
292impl<T: Debug + Display + Clone + PartialOrd> Range<T> for WithinRange<T> {
293    type Error = OutsideRangeError<T>;
294
295    fn value_is_valid(&self, value: &T) -> bool {
296        self.lower_bound.value_is_valid(value) && self.upper_bound.value_is_valid(value)
297    }
298
299    fn check_value_is_valid(&self, value: &T) -> Result<(), Self::Error> {
300        if self.value_is_valid(value) {
301            return Ok(());
302        }
303        Err(OutsideRangeError {
304            value: value.clone(),
305            valid_range: self.clone(),
306        })
307    }
308}
309
310///
311/// A [`Range`] implementation to verify a value is outside two reference values
312#[derive(Debug, Clone)]
313pub struct OutsideRange<T>
314where
315    T: Debug + Display + Clone + PartialOrd,
316{
317    pub(crate) lower_bound: LessThanValue<T>,
318    pub(crate) upper_bound: GreaterThanValue<T>,
319}
320
321impl<T: Debug + Display + Clone + PartialOrd> OutsideRange<T> {
322    /// Creates a new Outside Range.  Valid values must be less than the lower bound, or greater
323    /// than the upper bound.  Both bounds specify the first invalid values ob either end.
324    #[must_use]
325    pub fn new(lower_bound: T, upper_bound: T) -> Self {
326        Self {
327            lower_bound: LessThanValue::new(lower_bound),
328            upper_bound: GreaterThanValue::new(upper_bound),
329        }
330    }
331
332    /// Returns the lower bound of this range, valid values are less than this value
333    pub fn lower_bound(&self) -> &LessThanValue<T> {
334        &self.lower_bound
335    }
336
337    /// Returns the upper bound of this range, valid values are greater than this value
338    pub fn upper_bound(&self) -> &GreaterThanValue<T> {
339        &self.upper_bound
340    }
341}
342
343impl<T: Debug + Display + Clone + PartialOrd> Range<T> for OutsideRange<T> {
344    type Error = InsideRangeError<T>;
345
346    fn value_is_valid(&self, value: &T) -> bool {
347        self.lower_bound.value_is_valid(value) || self.upper_bound.value_is_valid(value)
348    }
349
350    fn check_value_is_valid(&self, value: &T) -> Result<(), Self::Error> {
351        if !self.lower_bound.value_is_valid(value) && !self.upper_bound.value_is_valid(value) {
352            return InsideRangeError::err(value.clone(), self.clone());
353        }
354        Ok(())
355    }
356}
357
358///
359/// An error type to indicate that the checked value is outside the specified
360/// value range
361#[derive(Debug, Clone)]
362pub struct OutsideRangeError<T>
363where
364    T: Debug + Display + Clone + PartialOrd,
365{
366    pub(crate) value: T,
367    pub(crate) valid_range: WithinRange<T>,
368}
369
370impl<T: Debug + Display + Clone + PartialOrd> OutsideRangeError<T> {
371    pub fn new(value: T, valid_range: WithinRange<T>) -> Self {
372        Self { value, valid_range }
373    }
374
375    pub fn err<O>(value: T, valid_range: WithinRange<T>) -> Result<O, Self> {
376        Err(Self::new(value, valid_range))
377    }
378}
379
380impl<T: Debug + Display + Clone + PartialOrd> Display for OutsideRangeError<T> {
381    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
382        f.write_fmt(format_args!(
383            "Value {} is outside valid range {} -> {}",
384            self.value, self.valid_range.lower_bound.value, self.valid_range.upper_bound.value
385        ))
386    }
387}
388#[cfg(feature = "std")]
389impl<T: Debug + Display + Clone + PartialOrd> std::error::Error for OutsideRangeError<T> {}
390
391///
392/// An error type to indicate that the value is inside the prohibited value range
393#[derive(Debug, Clone)]
394pub struct InsideRangeError<T>
395where
396    T: Debug + Display + Clone + PartialOrd,
397{
398    pub(crate) value: T,
399    pub(crate) valid_range: OutsideRange<T>,
400}
401impl<T: Debug + Display + Clone + PartialOrd> InsideRangeError<T> {
402    pub fn new(value: T, valid_range: OutsideRange<T>) -> Self {
403        Self { value, valid_range }
404    }
405
406    pub fn err<O>(value: T, valid_range: OutsideRange<T>) -> Result<O, Self> {
407        Err(Self::new(value, valid_range))
408    }
409}
410
411impl<T: Debug + Display + Clone + PartialOrd> Display for InsideRangeError<T> {
412    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
413        f.write_fmt(format_args!(
414            "Value {} is inside invalid range {} -> {}",
415            self.value, self.valid_range.lower_bound.value, self.valid_range.upper_bound.value
416        ))
417    }
418}
419impl<T: Debug + Display + Clone + PartialOrd> core::error::Error for InsideRangeError<T> {}