1use crate::{ValidationError, Validator};
13
14pub trait HasLength {
28 fn length(&self) -> usize;
30}
31
32impl HasLength for str {
33 fn length(&self) -> usize {
34 self.chars().count()
35 }
36}
37
38impl<T> HasLength for [T] {
39 fn length(&self) -> usize {
40 self.len()
41 }
42}
43
44impl<U: HasLength + ?Sized> HasLength for &U {
45 fn length(&self) -> usize {
46 U::length(self)
47 }
48}
49
50#[cfg(feature = "alloc")]
51impl HasLength for alloc::string::String {
52 fn length(&self) -> usize {
53 self.as_str().chars().count()
54 }
55}
56
57#[cfg(feature = "alloc")]
58impl<T> HasLength for alloc::vec::Vec<T> {
59 fn length(&self) -> usize {
60 self.len()
61 }
62}
63
64pub struct NonEmpty;
77
78impl<T: HasLength + ?Sized> Validator<T> for NonEmpty {
79 type Error = ValidationError;
80
81 fn validate(value: &T) -> Result<(), Self::Error> {
82 if value.length() > 0 {
83 Ok(())
84 } else {
85 Err(ValidationError::new("non_empty", "value must not be empty"))
86 }
87 }
88}
89
90pub struct MinLen<const MIN: usize>;
102
103impl<const MIN: usize, T: HasLength + ?Sized> Validator<T> for MinLen<MIN> {
104 type Error = ValidationError;
105
106 fn validate(value: &T) -> Result<(), Self::Error> {
107 if value.length() >= MIN {
108 Ok(())
109 } else {
110 Err(ValidationError::new("min_len", "value is too short"))
111 }
112 }
113}
114
115pub struct MaxLen<const MAX: usize>;
127
128impl<const MAX: usize, T: HasLength + ?Sized> Validator<T> for MaxLen<MAX> {
129 type Error = ValidationError;
130
131 fn validate(value: &T) -> Result<(), Self::Error> {
132 if value.length() <= MAX {
133 Ok(())
134 } else {
135 Err(ValidationError::new("max_len", "value is too long"))
136 }
137 }
138}
139
140pub struct LenRange<const MIN: usize, const MAX: usize>;
154
155impl<const MIN: usize, const MAX: usize, T: HasLength + ?Sized> Validator<T>
156 for LenRange<MIN, MAX>
157{
158 type Error = ValidationError;
159
160 fn validate(value: &T) -> Result<(), Self::Error> {
161 let len = value.length();
162 if len >= MIN && len <= MAX {
163 Ok(())
164 } else {
165 Err(ValidationError::new(
166 "len_range",
167 "value length is out of range",
168 ))
169 }
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 #![allow(clippy::unwrap_used, clippy::expect_used)]
176
177 use super::*;
178
179 #[test]
180 fn has_length_counts_chars_and_elements() {
181 assert_eq!("héllo".length(), 5);
182 assert_eq!([1, 2, 3][..].length(), 3);
183
184 let slice: &[u8] = b"abc";
186 assert_eq!(slice.length(), 3);
187 }
188
189 #[test]
190 fn non_empty_rules() {
191 assert!(NonEmpty::validate("x").is_ok());
192 assert_eq!(NonEmpty::validate("").unwrap_err().code(), "non_empty");
193 }
194
195 #[test]
196 fn min_max_len_boundaries() {
197 assert!(MinLen::<2>::validate("ab").is_ok());
198 assert!(MinLen::<2>::validate("a").is_err());
199 assert!(MaxLen::<2>::validate("ab").is_ok());
200 assert!(MaxLen::<2>::validate("abc").is_err());
201 }
202
203 #[test]
204 fn len_range_inclusive_bounds() {
205 assert!(LenRange::<2, 4>::validate("ab").is_ok());
206 assert!(LenRange::<2, 4>::validate("abcd").is_ok());
207 assert!(LenRange::<2, 4>::validate("a").is_err());
208 assert!(LenRange::<2, 4>::validate("abcde").is_err());
209 }
210}