validator_async/validation/
range.rs

1/// Validates that the given `value` is inside the defined range.
2/// The `max`, `min`, `exclusive_max` and `exclusive_min` parameters are
3/// optional and will only be validated if they are not `None`
4pub trait ValidateRange<T> {
5    fn validate_range(
6        &self,
7        min: Option<T>,
8        max: Option<T>,
9        exclusive_min: Option<T>,
10        exclusive_max: Option<T>,
11    ) -> bool {
12        if let Some(max) = max {
13            if let Some(gt) = self.greater_than(max) {
14                if gt {
15                    return false;
16                }
17            }
18        }
19
20        if let Some(min) = min {
21            if let Some(lt) = self.less_than(min) {
22                if lt {
23                    return false;
24                }
25            }
26        }
27
28        if let Some(exclusive_max) = exclusive_max {
29            if let Some(lt) = self.less_than(exclusive_max) {
30                if !lt {
31                    return false;
32                }
33            }
34        }
35
36        if let Some(exclusive_min) = exclusive_min {
37            if let Some(gt) = self.greater_than(exclusive_min) {
38                if !gt {
39                    return false;
40                }
41            }
42        }
43
44        true
45    }
46    fn greater_than(&self, max: T) -> Option<bool>;
47    fn less_than(&self, min: T) -> Option<bool>;
48}
49
50pub trait ValidateRangeType {}
51
52impl<T> ValidateRange<T> for T
53where
54    T: PartialEq + PartialOrd + ValidateRangeType,
55{
56    fn greater_than(&self, max: T) -> Option<bool> {
57        Some(self > &max)
58    }
59
60    fn less_than(&self, min: T) -> Option<bool> {
61        Some(self < &min)
62    }
63}
64
65impl<T> ValidateRange<T> for Option<T>
66where
67    T: PartialEq + PartialOrd + ValidateRangeType,
68{
69    fn greater_than(&self, max: T) -> Option<bool> {
70        self.as_ref().map(|r| r > &max)
71    }
72
73    fn less_than(&self, min: T) -> Option<bool> {
74        self.as_ref().map(|r| r < &min)
75    }
76}
77
78impl<T> ValidateRange<T> for Option<Option<T>>
79where
80    T: PartialEq + PartialOrd + ValidateRangeType,
81{
82    fn greater_than(&self, max: T) -> Option<bool> {
83        if let Some(r) = self {
84            r.as_ref().map(|r| r > &max)
85        } else {
86            None
87        }
88    }
89
90    fn less_than(&self, min: T) -> Option<bool> {
91        if let Some(r) = self {
92            r.as_ref().map(|r| r < &min)
93        } else {
94            None
95        }
96    }
97}
98
99macro_rules! impl_val_range {
100    ($t:tt) => {
101        impl ValidateRange<$t> for $t {
102            fn greater_than(&self, max: $t) -> Option<bool> {
103                Some(self > &max)
104            }
105
106            fn less_than(&self, min: $t) -> Option<bool> {
107                Some(self < &min)
108            }
109        }
110
111        impl ValidateRange<$t> for Option<$t> {
112            fn greater_than(&self, max: $t) -> Option<bool> {
113                self.map(|r| r > max)
114            }
115
116            fn less_than(&self, min: $t) -> Option<bool> {
117                self.map(|r| r < min)
118            }
119        }
120
121        impl ValidateRange<$t> for Option<Option<$t>> {
122            fn greater_than(&self, max: $t) -> Option<bool> {
123                self.flatten().map(|r| r > max)
124            }
125
126            fn less_than(&self, min: $t) -> Option<bool> {
127                self.flatten().map(|r| r < min)
128            }
129        }
130    };
131}
132
133impl_val_range!(u8);
134impl_val_range!(u16);
135impl_val_range!(u32);
136impl_val_range!(u64);
137impl_val_range!(u128);
138impl_val_range!(usize);
139impl_val_range!(i8);
140impl_val_range!(i16);
141impl_val_range!(i32);
142impl_val_range!(i64);
143impl_val_range!(i128);
144impl_val_range!(isize);
145impl_val_range!(f32);
146impl_val_range!(f64);
147
148#[cfg(test)]
149mod tests {
150    use crate::validation::range::ValidateRangeType;
151
152    use super::ValidateRange;
153
154    #[test]
155    fn test_validate_range_generic_ok() {
156        // Unspecified generic type:
157        assert!(10.validate_range(Some(-10), Some(10), None, None));
158        assert!(0.0.validate_range(Some(0.0), Some(10.0), None, None));
159
160        // Specified type:
161        assert!(5u8.validate_range(Some(0), Some(255), None, None));
162        assert!(4u16.validate_range(Some(0), Some(16), None, None));
163        assert!(6u32.validate_range(Some(0), Some(23), None, None));
164    }
165
166    #[test]
167    fn test_validate_range_generic_fail() {
168        assert!(!5.validate_range(Some(17), Some(19), None, None));
169        assert!(!(-1.0).validate_range(Some(0.0), Some(10.0), None, None));
170    }
171
172    #[test]
173    fn test_validate_range_generic_min_only() {
174        assert!(!5.validate_range(Some(10), None, None, None));
175        assert!(15.validate_range(Some(10), None, None, None));
176    }
177
178    #[test]
179    fn test_validate_range_generic_max_only() {
180        assert!(5.validate_range(None, Some(10), None, None));
181        assert!(!15.validate_range(None, Some(10), None, None));
182    }
183
184    #[test]
185    fn test_validate_range_generic_exc_ok() {
186        assert!(6.validate_range(None, None, Some(5), Some(7)));
187        assert!(0.0001.validate_range(None, None, Some(0.0), Some(1.0)));
188    }
189
190    #[test]
191    fn test_validate_range_generic_exc_fail() {
192        assert!(!5.validate_range(None, None, Some(5), None));
193    }
194
195    #[test]
196    fn test_validate_range_generic_exclusive_max_only() {
197        assert!(!10.validate_range(None, None, None, Some(10)));
198        assert!(9.validate_range(None, None, None, Some(10)));
199    }
200
201    #[test]
202    fn test_validate_range_generic_exclusive_min_only() {
203        assert!(!10.validate_range(None, None, Some(10), None));
204        assert!(9.validate_range(None, None, Some(8), None));
205    }
206
207    #[test]
208    fn test_validate_range_with_enums() {
209        #[derive(PartialEq, PartialOrd)]
210        enum Test {
211            One,
212            Two,
213            Three,
214            Four,
215        }
216
217        impl ValidateRangeType for Test {}
218
219        assert!(Test::Two.validate_range(Some(Test::One), Some(Test::Three), None, None));
220        assert!(!Test::Four.validate_range(Some(Test::One), Some(Test::Three), None, None));
221    }
222
223    #[test]
224    fn test_validate_range_with_option() {
225        assert!(Some(5).validate_range(Some(1), Some(10), None, None));
226        assert!(!Some(11).validate_range(Some(1), Some(10), None, None));
227    }
228
229    #[test]
230    fn test_validate_range_with_none_value() {
231        let none: Option<usize> = None;
232        let none_none: Option<Option<usize>> = None;
233        assert!(none.validate_range(Some(1), Some(10), None, None));
234        assert!(none.validate_range(Some(1), None, None, Some(10)));
235        assert!(none_none.validate_range(Some(1), Some(10), None, None));
236    }
237}