1use std::fmt::Display;
16
17use crate::error::Error;
18
19#[inline]
20pub fn apply<T: Bounds>(
21 v: &T,
22 (min, max): (Option<T::Size>, Option<T::Size>),
23) -> Result<(), Error> {
24 let min = min.unwrap_or(T::MIN);
25 let max = max.unwrap_or(T::MAX);
26 if let Err(e) = v.validate_bounds(min, max) {
27 match e {
28 OutOfBounds::Lower => return Err(Error::new(format!("lower than {min}"))),
29 OutOfBounds::Upper => return Err(Error::new(format!("greater than {max}"))),
30 }
31 }
32 Ok(())
33}
34
35pub trait Bounds: PartialOrd {
36 type Size: Copy + Sized + Display;
37
38 const MIN: Self::Size;
39 const MAX: Self::Size;
40
41 fn validate_bounds(
42 &self,
43 lower_bound: Self::Size,
44 upper_bound: Self::Size,
45 ) -> Result<(), OutOfBounds>;
46}
47
48pub enum OutOfBounds {
49 Lower,
50 Upper,
51}
52
53macro_rules! impl_for {
54 ($($T:ty),*) => {
55 $(
56 impl Bounds for $T {
57 type Size = $T;
58
59 const MIN: Self::Size = <$T>::MIN;
60 const MAX: Self::Size = <$T>::MAX;
61
62 fn validate_bounds(
63 &self,
64 lower_bound: Self::Size,
65 upper_bound: Self::Size,
66 ) -> Result<(), OutOfBounds> {
67 if self < &lower_bound {
68 Err(OutOfBounds::Lower)
69 } else if self > &upper_bound {
70 Err(OutOfBounds::Upper)
71 } else {
72 Ok(())
73 }
74 }
75 }
76 )*
77 };
78}
79
80impl_for!(u8, u16, u32, u64, usize, u128, i8, i16, i32, i64, isize, i128, f32, f64);
81
82#[cfg(feature = "rust_decimal")]
83impl_for!(rust_decimal::Decimal);
84
85impl<T: Bounds> Bounds for Option<T> {
86 type Size = T::Size;
87
88 const MIN: Self::Size = T::MIN;
89 const MAX: Self::Size = T::MAX;
90
91 fn validate_bounds(
92 &self,
93 lower_bound: Self::Size,
94 upper_bound: Self::Size,
95 ) -> Result<(), OutOfBounds> {
96 match self {
97 Some(value) => value.validate_bounds(lower_bound, upper_bound),
98 None => Ok(()),
99 }
100 }
101}