mx20022_validate/rules/
length.rs1use crate::error::{Severity, ValidationError};
4use crate::rules::Rule;
5
6pub struct MinLengthRule {
19 min: usize,
20}
21
22impl MinLengthRule {
23 pub fn new(min: usize) -> Self {
25 Self { min }
26 }
27}
28
29impl Rule for MinLengthRule {
30 fn id(&self) -> &'static str {
31 "MIN_LENGTH"
32 }
33
34 fn validate(&self, value: &str, path: &str) -> Vec<ValidationError> {
35 let len = value.chars().count();
37 if len < self.min {
38 vec![ValidationError::new(
39 path,
40 Severity::Error,
41 "MIN_LENGTH",
42 format!(
43 "Value length {len} is less than minimum length {}",
44 self.min
45 ),
46 )]
47 } else {
48 vec![]
49 }
50 }
51}
52
53pub struct MaxLengthRule {
66 max: usize,
67}
68
69impl MaxLengthRule {
70 pub fn new(max: usize) -> Self {
72 Self { max }
73 }
74}
75
76impl Rule for MaxLengthRule {
77 fn id(&self) -> &'static str {
78 "MAX_LENGTH"
79 }
80
81 fn validate(&self, value: &str, path: &str) -> Vec<ValidationError> {
82 let len = value.chars().count();
83 if len > self.max {
84 vec![ValidationError::new(
85 path,
86 Severity::Error,
87 "MAX_LENGTH",
88 format!("Value length {len} exceeds maximum length {}", self.max),
89 )]
90 } else {
91 vec![]
92 }
93 }
94}
95
96pub struct LengthRangeRule {
112 min: usize,
113 max: usize,
114}
115
116impl LengthRangeRule {
117 pub fn new(min: usize, max: usize) -> Self {
119 Self { min, max }
120 }
121}
122
123impl Rule for LengthRangeRule {
124 fn id(&self) -> &'static str {
125 "LENGTH_RANGE"
126 }
127
128 fn validate(&self, value: &str, path: &str) -> Vec<ValidationError> {
129 let len = value.chars().count();
130 let mut errors = vec![];
131 if len < self.min {
132 errors.push(ValidationError::new(
133 path,
134 Severity::Error,
135 "MIN_LENGTH",
136 format!(
137 "Value length {len} is less than minimum length {}",
138 self.min
139 ),
140 ));
141 }
142 if len > self.max {
143 errors.push(ValidationError::new(
144 path,
145 Severity::Error,
146 "MAX_LENGTH",
147 format!("Value length {len} exceeds maximum length {}", self.max),
148 ));
149 }
150 errors
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::rules::Rule;
158
159 #[test]
162 fn min_length_passes_when_equal() {
163 let rule = MinLengthRule::new(3);
164 assert!(rule.validate("abc", "/p").is_empty());
165 }
166
167 #[test]
168 fn min_length_passes_when_longer() {
169 let rule = MinLengthRule::new(3);
170 assert!(rule.validate("abcdef", "/p").is_empty());
171 }
172
173 #[test]
174 fn min_length_fails_when_shorter() {
175 let rule = MinLengthRule::new(3);
176 let errors = rule.validate("ab", "/p");
177 assert_eq!(errors.len(), 1);
178 assert_eq!(errors[0].rule_id, "MIN_LENGTH");
179 }
180
181 #[test]
182 fn min_length_zero_always_passes() {
183 let rule = MinLengthRule::new(0);
184 assert!(rule.validate("", "/p").is_empty());
185 }
186
187 #[test]
190 fn max_length_passes_when_equal() {
191 let rule = MaxLengthRule::new(35);
192 assert!(rule.validate(&"x".repeat(35), "/p").is_empty());
193 }
194
195 #[test]
196 fn max_length_passes_when_shorter() {
197 let rule = MaxLengthRule::new(35);
198 assert!(rule.validate("short", "/p").is_empty());
199 }
200
201 #[test]
202 fn max_length_fails_when_longer() {
203 let rule = MaxLengthRule::new(35);
204 let errors = rule.validate(&"x".repeat(36), "/p");
205 assert_eq!(errors.len(), 1);
206 assert_eq!(errors[0].rule_id, "MAX_LENGTH");
207 }
208
209 #[test]
210 fn max_length_empty_string_passes_when_max_positive() {
211 let rule = MaxLengthRule::new(5);
212 assert!(rule.validate("", "/p").is_empty());
213 }
214
215 #[test]
218 fn range_passes_within_bounds() {
219 let rule = LengthRangeRule::new(1, 35);
220 assert!(rule.validate("hello world", "/p").is_empty());
221 }
222
223 #[test]
224 fn range_fails_below_min() {
225 let rule = LengthRangeRule::new(1, 35);
226 let errors = rule.validate("", "/p");
227 assert_eq!(errors.len(), 1);
228 assert_eq!(errors[0].rule_id, "MIN_LENGTH");
229 }
230
231 #[test]
232 fn range_fails_above_max() {
233 let rule = LengthRangeRule::new(1, 5);
234 let errors = rule.validate("toolong", "/p");
235 assert_eq!(errors.len(), 1);
236 assert_eq!(errors[0].rule_id, "MAX_LENGTH");
237 }
238
239 #[test]
240 fn length_counts_unicode_code_points_not_bytes() {
241 let rule = MaxLengthRule::new(1);
243 assert!(rule.validate("é", "/p").is_empty());
244 }
245}