ai_lib_rust/structured/
error.rs1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ValidationError {
10 pub message: String,
12 pub path: Option<String>,
14 pub value: Option<serde_json::Value>,
16}
17
18impl ValidationError {
19 pub fn new(
27 message: impl Into<String>,
28 path: Option<String>,
29 value: Option<serde_json::Value>,
30 ) -> Self {
31 Self {
32 message: message.into(),
33 path,
34 value,
35 }
36 }
37
38 pub fn with_path(message: impl Into<String>, path: String) -> Self {
40 Self {
41 message: message.into(),
42 path: Some(path),
43 value: None,
44 }
45 }
46
47 pub fn without_path(message: impl Into<String>) -> Self {
49 Self {
50 message: message.into(),
51 path: None,
52 value: None,
53 }
54 }
55}
56
57impl fmt::Display for ValidationError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match &self.path {
60 Some(path) => write!(f, "{}: {}", path, self.message),
61 None => write!(f, "{}", self.message),
62 }
63 }
64}
65
66impl std::error::Error for ValidationError {}
67
68#[derive(Debug, Clone, PartialEq)]
70pub struct ValidationResult {
71 pub valid: bool,
73 pub errors: Vec<ValidationError>,
75 pub data: Option<serde_json::Value>,
77}
78
79impl ValidationResult {
80 pub fn success(data: serde_json::Value) -> Self {
82 Self {
83 valid: true,
84 errors: Vec::new(),
85 data: Some(data),
86 }
87 }
88
89 pub fn failure(errors: Vec<ValidationError>) -> Self {
91 Self {
92 valid: false,
93 errors,
94 data: None,
95 }
96 }
97
98 pub fn from_error(error: ValidationError) -> Self {
100 Self {
101 valid: false,
102 errors: vec![error],
103 data: None,
104 }
105 }
106
107 pub fn from_messages(messages: Vec<String>) -> Self {
109 Self {
110 valid: false,
111 errors: messages
112 .into_iter()
113 .map(ValidationError::without_path)
114 .collect(),
115 data: None,
116 }
117 }
118
119 pub fn is_valid(&self) -> bool {
121 self.valid
122 }
123
124 pub fn data(&self) -> Option<&serde_json::Value> {
128 self.data.as_ref()
129 }
130
131 pub fn error_messages(&self) -> Vec<String> {
133 self.errors.iter().map(|e| e.to_string()).collect()
134 }
135
136 pub fn merge(results: Vec<ValidationResult>) -> Self {
140 let mut all_errors = Vec::new();
141 let mut all_valid = true;
142 let mut final_data = None;
143 let any_results = !results.is_empty();
144
145 for result in results {
146 if !result.valid {
147 all_valid = false;
148 all_errors.extend(result.errors);
149 } else if final_data.is_none() {
150 final_data = result.data;
151 }
152 }
153
154 Self {
155 valid: all_valid,
156 errors: all_errors,
157 data: if all_valid && any_results {
158 final_data
159 } else {
160 None
161 },
162 }
163 }
164
165 pub fn into_result(self) -> Result<serde_json::Value, Vec<ValidationError>> {
167 if self.valid {
168 Ok(self.data.unwrap_or(serde_json::Value::Null))
169 } else {
170 Err(self.errors)
171 }
172 }
173}
174
175impl From<ValidationError> for ValidationResult {
176 fn from(error: ValidationError) -> Self {
177 Self::from_error(error)
178 }
179}
180
181impl From<Vec<ValidationError>> for ValidationResult {
182 fn from(errors: Vec<ValidationError>) -> Self {
183 Self::failure(errors)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_validation_error_display_without_path() {
193 let error = ValidationError::without_path("Invalid type");
194 assert_eq!(error.to_string(), "Invalid type");
195 assert!(error.path.is_none());
196 }
197
198 #[test]
199 fn test_validation_error_display_with_path() {
200 let error = ValidationError::with_path("Invalid type", "user.name".to_string());
201 assert_eq!(error.to_string(), "user.name: Invalid type");
202 assert_eq!(error.path, Some("user.name".to_string()));
203 }
204
205 #[test]
206 fn test_validation_result_success() {
207 let data = serde_json::json!({"name": "Alice"});
208 let result = ValidationResult::success(data.clone());
209
210 assert!(result.is_valid());
211 assert_eq!(result.data(), Some(&data));
212 assert!(result.errors.is_empty());
213 }
214
215 #[test]
216 fn test_validation_result_failure() {
217 let errors = vec![
218 ValidationError::with_path("Missing field", "user.name".to_string()),
219 ValidationError::with_path("Invalid type", "user.age".to_string()),
220 ];
221 let result = ValidationResult::failure(errors.clone());
222
223 assert!(!result.is_valid());
224 assert!(result.data.is_none());
225 assert_eq!(result.errors.len(), 2);
226 }
227
228 #[test]
229 fn test_validation_result_from_error() {
230 let error = ValidationError::without_path("Test error");
231 let result = ValidationResult::from_error(error.clone());
232
233 assert!(!result.is_valid());
234 assert_eq!(result.errors.len(), 1);
235 let first_error = &result.errors[0];
236 assert!(first_error.path.is_none());
237 }
238
239 #[test]
240 fn test_validation_result_from_messages() {
241 let messages = vec!["Error 1".to_string(), "Error 2".to_string()];
242 let result = ValidationResult::from_messages(messages);
243
244 assert!(!result.is_valid());
245 assert_eq!(result.errors.len(), 2);
246 assert!(result.errors[0].path.is_none());
247 }
248
249 #[test]
250 fn test_validation_result_merge_all_success() {
251 let result1 = ValidationResult::success(serde_json::json!(1));
252 let result2 = ValidationResult::success(serde_json::json!(2));
253
254 let merged = ValidationResult::merge(vec![result1, result2]);
255 assert!(merged.is_valid());
256 }
257
258 #[test]
259 fn test_validation_result_merge_one_failure() {
260 let result1 = ValidationResult::success(serde_json::json!(1));
261 let error = ValidationError::without_path("Test error");
262 let result2 = ValidationResult::from_error(error);
263
264 let merged = ValidationResult::merge(vec![result1, result2]);
265 assert!(!merged.is_valid());
266 assert_eq!(merged.errors.len(), 1);
267 }
268
269 #[test]
270 fn test_validation_result_into_result_success() {
271 let data = serde_json::json!({"test": 123});
272 let result = ValidationResult::success(data.clone());
273
274 assert_eq!(result.into_result(), Ok(data));
275 }
276
277 #[test]
278 fn test_validation_result_into_result_failure() {
279 let errors = vec![ValidationError::without_path("Test error")];
280 let result = ValidationResult::failure(errors.clone());
281
282 assert_eq!(result.into_result(), Err(errors));
283 }
284
285 #[test]
286 fn test_error_messages() {
287 let errors = vec![
288 ValidationError::with_path("Error 1", "path1".to_string()),
289 ValidationError::without_path("Error 2"),
290 ];
291 let result = ValidationResult::failure(errors);
292
293 let messages = result.error_messages();
294 assert_eq!(messages.len(), 2);
295 assert_eq!(messages[0], "path1: Error 1");
296 assert_eq!(messages[1], "Error 2");
297 }
298}