agtrace_engine/diagnostics/
validator.rs1use std::collections::HashMap;
2
3#[derive(Debug)]
4pub struct DiagnoseResult {
5 pub provider_name: String,
6 pub total_files: usize,
7 pub successful: usize,
8 pub failures: HashMap<FailureType, Vec<FailureExample>>,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub enum FailureType {
13 MissingField(String),
14 TypeMismatch(String),
15 ParseError,
16}
17
18impl std::fmt::Display for FailureType {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 FailureType::MissingField(field) => write!(f, "missing_field ({})", field),
22 FailureType::TypeMismatch(field) => write!(f, "type_mismatch ({})", field),
23 FailureType::ParseError => write!(f, "parse_error"),
24 }
25 }
26}
27
28#[derive(Debug, Clone)]
29pub struct FailureExample {
30 pub path: String,
31 pub reason: String,
32}
33
34pub fn categorize_parse_error(error_msg: &str) -> (FailureType, String) {
35 if error_msg.contains("missing field") {
36 if let Some(field) = extract_field_name(error_msg) {
37 (
38 FailureType::MissingField(field.clone()),
39 format!("Missing required field: {}", field),
40 )
41 } else {
42 (FailureType::ParseError, error_msg.to_string())
43 }
44 } else if error_msg.contains("expected") || error_msg.contains("invalid type") {
45 if let Some(field) = extract_field_name(error_msg) {
46 (
47 FailureType::TypeMismatch(field.clone()),
48 format!("Type mismatch for field: {}", field),
49 )
50 } else {
51 (FailureType::ParseError, error_msg.to_string())
52 }
53 } else {
54 (FailureType::ParseError, error_msg.to_string())
55 }
56}
57
58fn extract_field_name(error_msg: &str) -> Option<String> {
59 if let Some(start) = error_msg.find("field `") {
60 let rest = &error_msg[start + 7..];
61 if let Some(end) = rest.find('`') {
62 return Some(rest[..end].to_string());
63 }
64 }
65 None
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_categorize_missing_field() {
74 let error = "missing field `source` at line 1 column 2";
75 let (failure_type, reason) = categorize_parse_error(error);
76 assert_eq!(
77 failure_type,
78 FailureType::MissingField("source".to_string())
79 );
80 assert_eq!(reason, "Missing required field: source");
81 }
82
83 #[test]
84 fn test_categorize_type_mismatch() {
85 let error = "invalid type for field `timestamp`: expected string, got number";
86 let (failure_type, reason) = categorize_parse_error(error);
87 assert_eq!(
88 failure_type,
89 FailureType::TypeMismatch("timestamp".to_string())
90 );
91 assert_eq!(reason, "Type mismatch for field: timestamp");
92 }
93
94 #[test]
95 fn test_categorize_generic_parse_error() {
96 let error = "unexpected character at line 1";
97 let (failure_type, _reason) = categorize_parse_error(error);
98 assert_eq!(failure_type, FailureType::ParseError);
99 }
100
101 #[test]
102 fn test_extract_field_name() {
103 assert_eq!(
104 extract_field_name("missing field `source`"),
105 Some("source".to_string())
106 );
107 assert_eq!(
108 extract_field_name("field `timestamp` has wrong type"),
109 Some("timestamp".to_string())
110 );
111 assert_eq!(extract_field_name("no field here"), None);
112 }
113}