1use crate::context::ValidationContext;
11use crate::error::ValidationErrors;
12use crate::path::FieldPath;
13use crate::result::ValidationResult;
14
15pub trait Validate {
44 fn validate(&self) -> ValidationResult<()> {
46 self.validate_with_context(&ValidationContext::default())
47 }
48
49 fn validate_with_context(&self, ctx: &ValidationContext) -> ValidationResult<()>;
53
54 fn validate_and_transform(&self) -> ValidationResult<Self>
59 where
60 Self: Clone + Sized,
61 {
62 self.validate()?;
63 Ok(self.clone())
64 }
65}
66
67impl<T: Validate + ?Sized> Validate for Box<T> {
70 fn validate_with_context(&self, ctx: &ValidationContext) -> ValidationResult<()> {
71 (**self).validate_with_context(ctx)
72 }
73}
74
75impl<T: Validate + ?Sized> Validate for std::rc::Rc<T> {
76 fn validate_with_context(&self, ctx: &ValidationContext) -> ValidationResult<()> {
77 (**self).validate_with_context(ctx)
78 }
79}
80
81impl<T: Validate + ?Sized> Validate for std::sync::Arc<T> {
82 fn validate_with_context(&self, ctx: &ValidationContext) -> ValidationResult<()> {
83 (**self).validate_with_context(ctx)
84 }
85}
86
87impl<T: Validate + ?Sized> Validate for &T {
88 fn validate_with_context(&self, ctx: &ValidationContext) -> ValidationResult<()> {
89 (**self).validate_with_context(ctx)
90 }
91}
92
93pub trait Rule<T: ?Sized>: Send + Sync {
123 fn validate(&self, value: &T, ctx: &ValidationContext) -> ValidationResult<()>;
125
126 fn validate_at(
128 &self,
129 value: &T,
130 path: &FieldPath,
131 ctx: &ValidationContext,
132 ) -> ValidationResult<()> {
133 self.validate(value, ctx).map_err(|mut errors| {
134 for error in &mut errors.errors {
136 let mut new_path = path.clone();
137 for segment in error.path.segments().iter().cloned() {
138 match segment {
139 crate::path::PathSegment::Field(f) => new_path.append_field(f),
140 crate::path::PathSegment::Index(i) => new_path.append_index(i),
141 crate::path::PathSegment::Key(k) => new_path.append_key(k),
142 }
143 }
144 error.path = new_path;
145 }
146 errors
147 })
148 }
149
150 fn name(&self) -> &'static str;
152
153 fn default_message(&self) -> String;
155
156 fn error_code(&self) -> String {
158 self.name().to_string()
159 }
160
161 fn is_transform(&self) -> bool {
163 false
164 }
165}
166
167pub trait Transform<T>: Send + Sync {
171 fn transform(&self, value: T) -> T;
173
174 fn name(&self) -> &'static str;
176}
177
178pub trait ValidateNested: Validate {
182 fn validate_nested(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()>;
184}
185
186pub trait ValidateDive<T: Validate> {
190 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()>;
192}
193
194impl<T: Validate> ValidateNested for T {
197 fn validate_nested(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
198 self.validate_with_context(ctx).map_err(|errors| {
199 let mut nested = ValidationErrors::new();
200 if let Some(field) = path.last_field_name() {
201 nested.add_nested_errors(field, errors);
202 } else {
203 nested.merge(errors);
204 }
205 nested
206 })
207 }
208}
209
210impl<T: Validate> ValidateDive<T> for Vec<T> {
211 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
212 let mut errors = std::collections::BTreeMap::new();
213
214 for (idx, item) in self.iter().enumerate() {
215 if let Err(item_errors) = item.validate_with_context(ctx) {
216 errors.insert(idx, Box::new(item_errors));
217 }
218 }
219
220 if errors.is_empty() {
221 Ok(())
222 } else {
223 let mut result = ValidationErrors::new();
224 if let Some(field) = path.last_field_name() {
225 result.add_list_errors(field, errors);
226 }
227 Err(result)
228 }
229 }
230}
231
232impl<T: Validate> ValidateDive<T> for Option<T> {
233 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
234 if let Some(value) = self {
235 value.validate_nested(path, ctx)
236 } else {
237 Ok(())
238 }
239 }
240}
241
242impl<K, V> ValidateDive<V> for std::collections::HashMap<K, V>
243where
244 K: std::fmt::Display + Eq + std::hash::Hash,
245 V: Validate,
246{
247 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
248 let mut errors = std::collections::BTreeMap::new();
249
250 for (key, value) in self.iter() {
251 if let Err(item_errors) = value.validate_with_context(ctx) {
252 errors.insert(key.to_string(), Box::new(item_errors));
253 }
254 }
255
256 if errors.is_empty() {
257 Ok(())
258 } else {
259 let mut result = ValidationErrors::new();
260 if let Some(field) = path.last_field_name() {
261 result.add_map_errors(field, errors);
262 }
263 Err(result)
264 }
265 }
266}
267
268impl<K, V> ValidateDive<V> for std::collections::BTreeMap<K, V>
269where
270 K: std::fmt::Display + Ord,
271 V: Validate,
272{
273 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
274 let mut errors = std::collections::BTreeMap::new();
275
276 for (key, value) in self.iter() {
277 if let Err(item_errors) = value.validate_with_context(ctx) {
278 errors.insert(key.to_string(), Box::new(item_errors));
279 }
280 }
281
282 if errors.is_empty() {
283 Ok(())
284 } else {
285 let mut result = ValidationErrors::new();
286 if let Some(field) = path.last_field_name() {
287 result.add_map_errors(field, errors);
288 }
289 Err(result)
290 }
291 }
292}
293
294impl<T: Validate + Eq + std::hash::Hash> ValidateDive<T> for std::collections::HashSet<T> {
295 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
296 let mut errors = std::collections::BTreeMap::new();
297
298 for (idx, item) in self.iter().enumerate() {
299 if let Err(item_errors) = item.validate_with_context(ctx) {
300 errors.insert(idx, Box::new(item_errors));
301 }
302 }
303
304 if errors.is_empty() {
305 Ok(())
306 } else {
307 let mut result = ValidationErrors::new();
308 if let Some(field) = path.last_field_name() {
309 result.add_list_errors(field, errors);
310 }
311 Err(result)
312 }
313 }
314}
315
316impl<T: Validate + Ord> ValidateDive<T> for std::collections::BTreeSet<T> {
317 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
318 let mut errors = std::collections::BTreeMap::new();
319
320 for (idx, item) in self.iter().enumerate() {
321 if let Err(item_errors) = item.validate_with_context(ctx) {
322 errors.insert(idx, Box::new(item_errors));
323 }
324 }
325
326 if errors.is_empty() {
327 Ok(())
328 } else {
329 let mut result = ValidationErrors::new();
330 if let Some(field) = path.last_field_name() {
331 result.add_list_errors(field, errors);
332 }
333 Err(result)
334 }
335 }
336}
337
338impl<T: Validate> ValidateDive<T> for Box<T> {
340 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
341 self.as_ref().validate_nested(path, ctx)
342 }
343}
344
345impl<T: Validate> ValidateDive<T> for std::rc::Rc<T> {
346 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
347 self.as_ref().validate_nested(path, ctx)
348 }
349}
350
351impl<T: Validate> ValidateDive<T> for std::sync::Arc<T> {
352 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
353 self.as_ref().validate_nested(path, ctx)
354 }
355}
356
357impl<T: Validate> ValidateDive<T> for [T] {
359 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
360 let mut errors = std::collections::BTreeMap::new();
361
362 for (idx, item) in self.iter().enumerate() {
363 if let Err(item_errors) = item.validate_with_context(ctx) {
364 errors.insert(idx, Box::new(item_errors));
365 }
366 }
367
368 if errors.is_empty() {
369 Ok(())
370 } else {
371 let mut result = ValidationErrors::new();
372 if let Some(field) = path.last_field_name() {
373 result.add_list_errors(field, errors);
374 }
375 Err(result)
376 }
377 }
378}
379
380impl<T: Validate, const N: usize> ValidateDive<T> for [T; N] {
382 fn validate_dive(&self, path: &FieldPath, ctx: &ValidationContext) -> ValidationResult<()> {
383 let mut errors = std::collections::BTreeMap::new();
384
385 for (idx, item) in self.iter().enumerate() {
386 if let Err(item_errors) = item.validate_with_context(ctx) {
387 errors.insert(idx, Box::new(item_errors));
388 }
389 }
390
391 if errors.is_empty() {
392 Ok(())
393 } else {
394 let mut result = ValidationErrors::new();
395 if let Some(field) = path.last_field_name() {
396 result.add_list_errors(field, errors);
397 }
398 Err(result)
399 }
400 }
401}
402
403pub struct CompositeRule<T: ?Sized> {
405 rules: Vec<Box<dyn Rule<T>>>,
406}
407
408impl<T: ?Sized> CompositeRule<T> {
409 pub fn new() -> Self {
411 Self { rules: Vec::new() }
412 }
413
414 pub fn push_rule(mut self, rule: impl Rule<T> + 'static) -> Self {
416 self.rules.push(Box::new(rule));
417 self
418 }
419}
420
421impl<T: ?Sized> Default for CompositeRule<T> {
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427impl<T: ?Sized + Sync> Rule<T> for CompositeRule<T> {
428 fn validate(&self, value: &T, ctx: &ValidationContext) -> ValidationResult<()> {
429 let mut all_errors = ValidationErrors::new();
430
431 for rule in &self.rules {
432 if let Err(errors) = rule.validate(value, ctx) {
433 all_errors.merge(errors);
434
435 if ctx.is_fail_fast() && !all_errors.is_empty() {
437 return Err(all_errors);
438 }
439 }
440 }
441
442 if all_errors.is_empty() {
443 Ok(())
444 } else {
445 Err(all_errors)
446 }
447 }
448
449 fn name(&self) -> &'static str {
450 "composite"
451 }
452
453 fn default_message(&self) -> String {
454 "Validation failed".to_string()
455 }
456}
457
458pub struct AnyRule<T: ?Sized> {
460 rules: Vec<Box<dyn Rule<T>>>,
461}
462
463impl<T: ?Sized> AnyRule<T> {
464 pub fn new() -> Self {
466 Self { rules: Vec::new() }
467 }
468
469 pub fn push_rule(mut self, rule: impl Rule<T> + 'static) -> Self {
471 self.rules.push(Box::new(rule));
472 self
473 }
474}
475
476impl<T: ?Sized> Default for AnyRule<T> {
477 fn default() -> Self {
478 Self::new()
479 }
480}
481
482impl<T: ?Sized + Sync> Rule<T> for AnyRule<T> {
483 fn validate(&self, value: &T, ctx: &ValidationContext) -> ValidationResult<()> {
484 if self.rules.is_empty() {
485 return Ok(());
486 }
487
488 for rule in &self.rules {
489 if rule.validate(value, ctx).is_ok() {
490 return Ok(()); }
492 }
493
494 Err(ValidationErrors::from_iter([crate::ValidationError::root(
496 "any",
497 self.default_message(),
498 )]))
499 }
500
501 fn name(&self) -> &'static str {
502 "any"
503 }
504
505 fn default_message(&self) -> String {
506 "Must satisfy at least one validation rule".to_string()
507 }
508}
509
510pub type AllRule<T> = CompositeRule<T>;
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516 use crate::ValidationError;
517
518 struct MockRule {
520 should_pass: bool,
521 }
522
523 impl Rule<str> for MockRule {
524 fn validate(&self, _value: &str, _ctx: &ValidationContext) -> ValidationResult<()> {
525 if self.should_pass {
526 Ok(())
527 } else {
528 Err(ValidationErrors::from_iter([ValidationError::new(
529 "",
530 "mock",
531 "Mock error",
532 )]))
533 }
534 }
535
536 fn name(&self) -> &'static str {
537 "mock"
538 }
539
540 fn default_message(&self) -> String {
541 "Mock error".to_string()
542 }
543 }
544
545 #[test]
546 fn test_composite_rule() {
547 let rule = CompositeRule::new()
548 .push_rule(MockRule { should_pass: true })
549 .push_rule(MockRule { should_pass: true });
550
551 let ctx = ValidationContext::default();
552 assert!(rule.validate("test", &ctx).is_ok());
553 }
554
555 #[test]
556 fn test_composite_rule_fails() {
557 let rule = CompositeRule::new()
558 .push_rule(MockRule { should_pass: true })
559 .push_rule(MockRule { should_pass: false });
560
561 let ctx = ValidationContext::default();
562 assert!(rule.validate("test", &ctx).is_err());
563 }
564
565 #[test]
566 fn test_any_rule() {
567 let rule = AnyRule::new()
568 .push_rule(MockRule { should_pass: false })
569 .push_rule(MockRule { should_pass: true });
570
571 let ctx = ValidationContext::default();
572 assert!(rule.validate("test", &ctx).is_ok());
573 }
574
575 #[test]
576 fn test_any_rule_all_fail() {
577 let rule = AnyRule::new()
578 .push_rule(MockRule { should_pass: false })
579 .push_rule(MockRule { should_pass: false });
580
581 let ctx = ValidationContext::default();
582 assert!(rule.validate("test", &ctx).is_err());
583 }
584}