1use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ValidationError {
10 pub path: String,
12 pub message: String,
14 pub rule: String,
16 pub value: Option<String>,
18}
19
20impl ValidationError {
21 pub fn new(
23 path: impl Into<String>,
24 message: impl Into<String>,
25 rule: impl Into<String>,
26 ) -> Self {
27 Self {
28 path: path.into(),
29 message: message.into(),
30 rule: rule.into(),
31 value: None,
32 }
33 }
34
35 pub fn with_value(mut self, value: impl Into<String>) -> Self {
37 self.value = Some(value.into());
38 self
39 }
40}
41
42impl fmt::Display for ValidationError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "{}: {}", self.path, self.message)
45 }
46}
47
48impl std::error::Error for ValidationError {}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct ValidationErrors {
53 pub errors: Vec<ValidationError>,
55}
56
57impl ValidationErrors {
58 pub fn new() -> Self {
60 Self { errors: Vec::new() }
61 }
62
63 pub fn push(&mut self, error: ValidationError) {
65 self.errors.push(error);
66 }
67
68 pub fn is_empty(&self) -> bool {
70 self.errors.is_empty()
71 }
72
73 pub fn len(&self) -> usize {
75 self.errors.len()
76 }
77}
78
79impl Default for ValidationErrors {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85impl fmt::Display for ValidationErrors {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 for (i, err) in self.errors.iter().enumerate() {
88 if i > 0 {
89 writeln!(f)?;
90 }
91 write!(f, "{}", err)?;
92 }
93 Ok(())
94 }
95}
96
97impl std::error::Error for ValidationErrors {}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_validation_error_creation() {
105 let err = ValidationError::new("email", "Invalid email", "email_pattern");
106 assert_eq!(err.path, "email");
107 assert_eq!(err.message, "Invalid email");
108 assert_eq!(err.rule, "email_pattern");
109 assert_eq!(err.value, None);
110 }
111
112 #[test]
113 fn test_validation_error_with_value() {
114 let err =
115 ValidationError::new("email", "Invalid email", "email_pattern").with_value("invalid@");
116 assert_eq!(err.value, Some("invalid@".to_string()));
117 }
118
119 #[test]
120 fn test_validation_error_display() {
121 let err = ValidationError::new("email", "Invalid email", "email_pattern");
122 assert_eq!(err.to_string(), "email: Invalid email");
123 }
124
125 #[test]
126 fn test_validation_errors_collection() {
127 let mut errors = ValidationErrors::new();
128 assert!(errors.is_empty());
129 assert_eq!(errors.len(), 0);
130
131 errors.push(ValidationError::new("email", "Invalid", "rule1"));
132 assert!(!errors.is_empty());
133 assert_eq!(errors.len(), 1);
134
135 errors.push(ValidationError::new("age", "Too young", "rule2"));
136 assert_eq!(errors.len(), 2);
137 }
138}