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
12pub 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}