validator_async/validation/
length.rs1use 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
12pub 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}