validator_async/validation/
length.rs

1use std::{
2    borrow::Cow,
3    cell::{Ref, RefMut},
4    collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
5    rc::Rc,
6    sync::Arc,
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>
18where
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
50            T: ValidateLength<u64>,
51        {
52            fn length(&self) -> Option<u64> {
53                T::length(self)
54            }
55        }
56    };
57}
58
59validate_type_that_derefs!(&T);
60validate_type_that_derefs!(Arc<T>);
61validate_type_that_derefs!(Box<T>);
62validate_type_that_derefs!(Rc<T>);
63validate_type_that_derefs!(Ref<'_, T>);
64validate_type_that_derefs!(RefMut<'_, T>);
65
66macro_rules! validate_type_with_chars {
67    ($type_:ty) => {
68        impl ValidateLength<u64> for $type_ {
69            fn length(&self) -> Option<u64> {
70                Some(self.chars().count() as u64)
71            }
72        }
73    };
74}
75
76validate_type_with_chars!(str);
77validate_type_with_chars!(&str);
78validate_type_with_chars!(String);
79
80macro_rules! validate_type_with_len {
81    ($type_:ty) => {
82        validate_type_with_len!($type_,);
83    };
84    ($type_:ty, $($generic:ident),*$(,)*) => {
85        impl<$($generic),*> ValidateLength<u64> for $type_ {
86            fn length(&self) -> Option<u64> {
87                Some(self.len() as u64)
88            }
89        }
90    };
91}
92
93validate_type_with_len!([T], T);
94validate_type_with_len!(BTreeSet<T>, T);
95validate_type_with_len!(BTreeMap<K, V>, K, V);
96validate_type_with_len!(HashSet<T, S>, T, S);
97validate_type_with_len!(HashMap<K, V, S>, K, V, S);
98validate_type_with_len!(Vec<T>, T);
99validate_type_with_len!(VecDeque<T>, T);
100#[cfg(feature = "indexmap")]
101validate_type_with_len!(IndexSet<T>, T);
102#[cfg(feature = "indexmap")]
103validate_type_with_len!(IndexMap<K, V>, K, V);
104
105impl<T> ValidateLength<u64> for Cow<'_, T>
106where
107    T: ToOwned + ?Sized,
108    for<'a> &'a T: ValidateLength<u64>,
109{
110    fn length(&self) -> Option<u64> {
111        self.as_ref().length()
112    }
113}
114
115impl<T> ValidateLength<u64> for Option<T>
116where
117    T: ValidateLength<u64>,
118{
119    fn length(&self) -> Option<u64> {
120        let Some(s) = self else {
121            return None;
122        };
123
124        T::length(s)
125    }
126}
127
128impl<T, const N: usize> ValidateLength<u64> for [T; N] {
129    fn length(&self) -> Option<u64> {
130        Some(N as u64)
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use std::borrow::Cow;
137
138    use super::ValidateLength;
139
140    #[test]
141    fn test_validate_length_equal_overrides_min_max() {
142        assert!("hello".validate_length(Some(1), Some(2), Some(5)));
143    }
144
145    #[test]
146    fn test_validate_length_string_min_max() {
147        assert!("hello".validate_length(Some(1), Some(10), None));
148    }
149
150    #[test]
151    fn test_validate_length_string_min_only() {
152        assert!(!"hello".validate_length(Some(10), None, None));
153    }
154
155    #[test]
156    fn test_validate_length_string_max_only() {
157        assert!(!"hello".validate_length(None, Some(1), None));
158    }
159
160    #[test]
161    fn test_validate_length_cow() {
162        let test: Cow<'static, str> = "hello".into();
163        assert!(test.validate_length(None, None, Some(5)));
164
165        let test: Cow<'static, str> = String::from("hello").into();
166        assert!(test.validate_length(None, None, Some(5)));
167    }
168
169    #[test]
170    fn test_validate_length_vec() {
171        assert!(vec![1, 2, 3].validate_length(None, None, Some(3)));
172    }
173
174    #[test]
175    fn test_validate_length_unicode_chars() {
176        assert!("日本".validate_length(None, None, Some(2)));
177    }
178
179    #[test]
180    fn test_validate_length_cow_unicode_chars() {
181        let test: Cow<'static, str> = "日本".into();
182        assert!(test.validate_length(None, None, Some(2)));
183
184        let test: Cow<'static, str> = String::from("日本").into();
185        assert!(test.validate_length(None, None, Some(2)));
186    }
187
188    #[test]
189    fn test_validate_length_trait_equal_overrides_min_max() {
190        assert!(String::from("hello").validate_length(Some(1), Some(2), Some(5)));
191    }
192
193    #[test]
194    fn test_validate_length_trait_string_min_max() {
195        assert!(String::from("hello").validate_length(Some(1), Some(10), None));
196    }
197
198    #[test]
199    fn test_validate_length_trait_string_min_only() {
200        assert!(!String::from("hello").validate_length(Some(10), None, None));
201    }
202
203    #[test]
204    fn test_validate_length_trait_string_max_only() {
205        assert!(!String::from("hello").validate_length(None, Some(1), None));
206    }
207
208    #[test]
209    fn test_validate_length_trait_cow() {
210        let test: Cow<'static, str> = "hello".into();
211        assert!(test.validate_length(None, None, Some(5)));
212
213        let test: Cow<'static, str> = String::from("hello").into();
214        assert!(test.validate_length(None, None, Some(5)));
215    }
216
217    #[test]
218    fn test_validate_length_trait_vec() {
219        assert!(vec![1, 2, 3].validate_length(None, None, Some(3)));
220    }
221
222    #[test]
223    fn test_validate_length_trait_unicode_chars() {
224        assert!(String::from("日本").validate_length(None, None, Some(2)));
225    }
226}