icydb_base/validator/
len.rs

1#![allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
2
3use crate::{core::traits::Validator, prelude::*};
4use std::{
5    collections::{HashMap, HashSet},
6    hash::BuildHasher,
7};
8
9///
10/// HasLen
11///
12
13#[allow(clippy::len_without_is_empty)]
14pub trait HasLen {
15    fn len(&self) -> usize;
16}
17
18impl HasLen for Blob {
19    fn len(&self) -> usize {
20        Self::len(self)
21    }
22}
23
24impl HasLen for str {
25    fn len(&self) -> usize {
26        Self::len(self)
27    }
28}
29
30impl HasLen for String {
31    fn len(&self) -> usize {
32        Self::len(self)
33    }
34}
35
36impl<T> HasLen for [T] {
37    fn len(&self) -> usize {
38        <[T]>::len(self)
39    }
40}
41
42impl<T> HasLen for Vec<T> {
43    fn len(&self) -> usize {
44        Self::len(self)
45    }
46}
47
48impl<T, S: BuildHasher> HasLen for HashSet<T, S> {
49    fn len(&self) -> usize {
50        Self::len(self)
51    }
52}
53
54impl<K, V, S: BuildHasher> HasLen for HashMap<K, V, S> {
55    fn len(&self) -> usize {
56        Self::len(self)
57    }
58}
59
60///
61/// Equal
62///
63
64#[validator]
65pub struct Equal {
66    target: i32,
67}
68
69impl Equal {
70    pub fn new(target: impl Into<i32>) -> Self {
71        let target = target.into();
72        assert!(target >= 0, "equal target must be non-negative");
73
74        Self { target }
75    }
76}
77
78impl<T: HasLen + ?Sized> Validator<T> for Equal {
79    fn validate(&self, t: &T) -> Result<(), String> {
80        let len = t.len() as i32;
81
82        if len == self.target {
83            Ok(())
84        } else {
85            Err(format!("length ({}) is not equal to {}", len, self.target))
86        }
87    }
88}
89
90///
91/// Min
92///
93
94#[validator]
95pub struct Min {
96    target: i32,
97}
98
99impl Min {
100    pub fn new(target: impl Into<i32>) -> Self {
101        let target = target.into();
102        assert!(target >= 0, "min target must be non-negative");
103
104        Self { target }
105    }
106}
107
108impl<T: HasLen + ?Sized> Validator<T> for Min {
109    fn validate(&self, t: &T) -> Result<(), String> {
110        let len = t.len() as i32;
111
112        if len < self.target {
113            Err(format!(
114                "length ({}) is lower than minimum of {}",
115                len, self.target
116            ))
117        } else {
118            Ok(())
119        }
120    }
121}
122
123///
124/// Max
125///
126
127#[validator]
128pub struct Max {
129    target: i32,
130}
131
132impl Max {
133    pub fn new(target: impl Into<i32>) -> Self {
134        let target = target.into();
135        assert!(target >= 0, "max target must be non-negative");
136
137        Self { target }
138    }
139}
140
141impl<T: HasLen + ?Sized> Validator<T> for Max {
142    fn validate(&self, t: &T) -> Result<(), String> {
143        let len = t.len() as i32;
144
145        if len > self.target {
146            Err(format!(
147                "length ({}) is greater than maximum of {}",
148                len, self.target
149            ))
150        } else {
151            Ok(())
152        }
153    }
154}
155
156///
157/// Range
158///
159
160#[validator]
161pub struct Range {
162    min: i32,
163    max: i32,
164}
165
166impl Range {
167    pub fn new(min: impl Into<i32>, max: impl Into<i32>) -> Self {
168        let (min, max) = (min.into(), max.into());
169        assert!(min >= 0, "min target must be non-negative");
170        assert!(max >= 0, "max target must be non-negative");
171        assert!(min <= max, "range requires min <= max");
172
173        Self { min, max }
174    }
175}
176
177impl<T: HasLen + ?Sized> Validator<T> for Range {
178    fn validate(&self, t: &T) -> Result<(), String> {
179        let len = t.len() as i32;
180
181        if len < self.min || len > self.max {
182            Err(format!(
183                "length ({len}) must be between {} and {} (inclusive)",
184                self.min, self.max
185            ))
186        } else {
187            Ok(())
188        }
189    }
190}
191
192///
193/// TESTS
194///
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_range_ok() {
202        let r = Range::new(2, 5);
203        assert!(r.validate("hey").is_ok()); // len = 3
204    }
205
206    #[test]
207    fn test_range_err() {
208        let r = Range::new(2, 5);
209        assert!(r.validate("hello world").is_err()); // len = 11
210    }
211}