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