validator/validation/
length.rs

1use std::{
2    borrow::Cow,
3    collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
4    rc::Rc,
5    sync::Arc,
6    cell::{Ref, RefMut},
7};
8
9#[cfg(feature = "indexmap")]
10use indexmap::{IndexMap, IndexSet};
11
12/// Validates the length of the value given.
13/// If the validator has `equal` set, it will ignore any `min` and `max` value.
14///
15/// If you apply it on String, don't forget that the length can be different
16/// from the number of visual characters for Unicode
17pub trait ValidateLength<T>
18    where
19        T: PartialEq + PartialOrd,
20{
21    fn validate_length(&self, min: Option<T>, max: Option<T>, equal: Option<T>) -> bool {
22        if let Some(length) = self.length() {
23            if let Some(eq) = equal {
24                return length == eq;
25            } else {
26                if let Some(m) = min {
27                    if length < m {
28                        return false;
29                    }
30                }
31                if let Some(m) = max {
32                    if length > m {
33                        return false;
34                    }
35                }
36            }
37            true
38        } else {
39            true
40        }
41    }
42
43    fn length(&self) -> Option<T>;
44}
45
46macro_rules! validate_type_that_derefs {
47    ($type_:ty) => {
48        impl<T> ValidateLength<u64> for $type_
49        where T: ValidateLength<u64> {
50            fn length(&self) -> Option<u64> {
51                T::length(self)
52            }
53        }
54    };
55}
56
57validate_type_that_derefs!(&T);
58validate_type_that_derefs!(Arc<T>);
59validate_type_that_derefs!(Box<T>);
60validate_type_that_derefs!(Rc<T>);
61validate_type_that_derefs!(Ref<'_, T>);
62validate_type_that_derefs!(RefMut<'_, T>);
63
64macro_rules! validate_type_with_chars {
65    ($type_:ty) => {
66        impl ValidateLength<u64> for $type_ {
67            fn length(&self) -> Option<u64> {
68                Some(self.chars().count() as u64)
69            }
70        }
71    };
72}
73
74validate_type_with_chars!(str);
75validate_type_with_chars!(&str);
76validate_type_with_chars!(String);
77
78macro_rules! validate_type_with_len {
79    ($type_:ty) => {
80        validate_type_with_len!($type_,);
81    };
82    ($type_:ty, $($generic:ident),*$(,)*) => {
83        impl<$($generic),*> ValidateLength<u64> for $type_ {
84            fn length(&self) -> Option<u64> {
85                Some(self.len() as u64)
86            }
87        }
88    };
89}
90
91validate_type_with_len!([T], T);
92validate_type_with_len!(BTreeSet<T>, T);
93validate_type_with_len!(BTreeMap<K, V>, K, V);
94validate_type_with_len!(HashSet<T, S>, T, S);
95validate_type_with_len!(HashMap<K, V, S>, K, V, S);
96validate_type_with_len!(Vec<T>, T);
97validate_type_with_len!(VecDeque<T>, T);
98#[cfg(feature = "indexmap")]
99validate_type_with_len!(IndexSet<T>, T);
100#[cfg(feature = "indexmap")]
101validate_type_with_len!(IndexMap<K, V>, K, V);
102
103impl<T> ValidateLength<u64> for Cow<'_, T>
104    where
105        T: ToOwned + ?Sized,
106        for<'a> &'a T: ValidateLength<u64>,
107{
108    fn length(&self) -> Option<u64> {
109        self.as_ref().length()
110    }
111}
112
113impl<T> ValidateLength<u64> for Option<T>
114    where T: ValidateLength<u64>,
115{
116    fn length(&self) -> Option<u64> {
117        let Some(s) = self else {
118            return None;
119        };
120
121        T::length(s)
122    }
123}
124
125impl<T, const N: usize> ValidateLength<u64> for [T; N] {
126    fn length(&self) -> Option<u64> {
127        Some(N as u64)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use std::borrow::Cow;
134
135    use super::ValidateLength;
136
137    #[test]
138    fn test_validate_length_equal_overrides_min_max() {
139        assert!("hello".validate_length(Some(1), Some(2), Some(5)));
140    }
141
142    #[test]
143    fn test_validate_length_string_min_max() {
144        assert!("hello".validate_length(Some(1), Some(10), None));
145    }
146
147    #[test]
148    fn test_validate_length_string_min_only() {
149        assert!(!"hello".validate_length(Some(10), None, None));
150    }
151
152    #[test]
153    fn test_validate_length_string_max_only() {
154        assert!(!"hello".validate_length(None, Some(1), None));
155    }
156
157    #[test]
158    fn test_validate_length_cow() {
159        let test: Cow<'static, str> = "hello".into();
160        assert!(test.validate_length(None, None, Some(5)));
161
162        let test: Cow<'static, str> = String::from("hello").into();
163        assert!(test.validate_length(None, None, Some(5)));
164    }
165
166    #[test]
167    fn test_validate_length_vec() {
168        assert!(vec![1, 2, 3].validate_length(None, None, Some(3)));
169    }
170
171    #[test]
172    fn test_validate_length_unicode_chars() {
173        assert!("日本".validate_length(None, None, Some(2)));
174    }
175
176    #[test]
177    fn test_validate_length_cow_unicode_chars() {
178        let test: Cow<'static, str> = "日本".into();
179        assert!(test.validate_length(None, None, Some(2)));
180
181        let test: Cow<'static, str> = String::from("日本").into();
182        assert!(test.validate_length(None, None, Some(2)));
183    }
184
185    #[test]
186    fn test_validate_length_trait_equal_overrides_min_max() {
187        assert!(String::from("hello").validate_length(Some(1), Some(2), Some(5)));
188    }
189
190    #[test]
191    fn test_validate_length_trait_string_min_max() {
192        assert!(String::from("hello").validate_length(Some(1), Some(10), None));
193    }
194
195    #[test]
196    fn test_validate_length_trait_string_min_only() {
197        assert!(!String::from("hello").validate_length(Some(10), None, None));
198    }
199
200    #[test]
201    fn test_validate_length_trait_string_max_only() {
202        assert!(!String::from("hello").validate_length(None, Some(1), None));
203    }
204
205    #[test]
206    fn test_validate_length_trait_cow() {
207        let test: Cow<'static, str> = "hello".into();
208        assert!(test.validate_length(None, None, Some(5)));
209
210        let test: Cow<'static, str> = String::from("hello").into();
211        assert!(test.validate_length(None, None, Some(5)));
212    }
213
214    #[test]
215    fn test_validate_length_trait_vec() {
216        assert!(vec![1, 2, 3].validate_length(None, None, Some(3)));
217    }
218
219    #[test]
220    fn test_validate_length_trait_unicode_chars() {
221        assert!(String::from("日本").validate_length(None, None, Some(2)));
222    }
223}