Skip to main content

data_protocol_validator/
errors.rs

1use std::collections::HashMap;
2
3/// Severity level for a validation error or warning.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum Severity {
6    Error,
7    Warning,
8}
9
10impl Severity {
11    pub fn as_str(&self) -> &'static str {
12        match self {
13            Severity::Error => "error",
14            Severity::Warning => "warning",
15        }
16    }
17}
18
19struct ErrorRegistryEntry {
20    severity: Severity,
21    message_template: &'static str,
22}
23
24/// Build the static error registry mapping error codes to their severity and
25/// message template.
26fn error_registry() -> HashMap<&'static str, ErrorRegistryEntry> {
27    let mut m = HashMap::new();
28    m.insert(
29        "E001",
30        ErrorRegistryEntry {
31            severity: Severity::Error,
32            message_template: "Type mismatch: expected {expected}, got {actual}",
33        },
34    );
35    m.insert(
36        "E002",
37        ErrorRegistryEntry {
38            severity: Severity::Error,
39            message_template: "Required property \"{property}\" is missing",
40        },
41    );
42    m.insert(
43        "E003",
44        ErrorRegistryEntry {
45            severity: Severity::Error,
46            message_template: "Additional property \"{property}\" is not allowed",
47        },
48    );
49    m.insert(
50        "E004",
51        ErrorRegistryEntry {
52            severity: Severity::Error,
53            message_template: "String constraint violation: {constraint}",
54        },
55    );
56    m.insert(
57        "E005",
58        ErrorRegistryEntry {
59            severity: Severity::Error,
60            message_template: "Number constraint violation: {constraint}",
61        },
62    );
63    m.insert(
64        "E006",
65        ErrorRegistryEntry {
66            severity: Severity::Error,
67            message_template: "Array constraint violation: {constraint}",
68        },
69    );
70    m.insert(
71        "E007",
72        ErrorRegistryEntry {
73            severity: Severity::Error,
74            message_template: "Object constraint violation: {constraint}",
75        },
76    );
77    m.insert(
78        "E008",
79        ErrorRegistryEntry {
80            severity: Severity::Error,
81            message_template: "Format validation failed: expected {format}, got \"{value}\"",
82        },
83    );
84    m.insert(
85        "E009",
86        ErrorRegistryEntry {
87            severity: Severity::Error,
88            message_template: "Value {value} does not match {constraint}",
89        },
90    );
91    m.insert(
92        "E010",
93        ErrorRegistryEntry {
94            severity: Severity::Error,
95            message_template: "Composition constraint \"{keyword}\" not satisfied",
96        },
97    );
98    m.insert(
99        "E011",
100        ErrorRegistryEntry {
101            severity: Severity::Error,
102            message_template: "Failed to resolve $ref: \"{ref}\"",
103        },
104    );
105    m.insert(
106        "E012",
107        ErrorRegistryEntry {
108            severity: Severity::Error,
109            message_template: "Invalid protocol: {reason}",
110        },
111    );
112    m.insert(
113        "W001",
114        ErrorRegistryEntry {
115            severity: Severity::Warning,
116            message_template: "Field \"{field}\" is deprecated: {reason}",
117        },
118    );
119    m.insert(
120        "W002",
121        ErrorRegistryEntry {
122            severity: Severity::Warning,
123            message_template: "Unknown extension property: \"{property}\"",
124        },
125    );
126    m
127}
128
129/// Format a message template by replacing `{key}` placeholders with values
130/// from the context map.
131fn format_template(template: &str, context: &HashMap<&str, String>) -> String {
132    let mut result = template.to_string();
133    for (key, value) in context {
134        let placeholder = format!("{{{}}}", key);
135        result = result.replace(&placeholder, value);
136    }
137    result
138}
139
140use crate::types::ValidationError;
141
142/// Create a `ValidationError` from an error code, JSON path, and context
143/// values that are interpolated into the message template.
144pub fn create_error(code: &str, path: &str, context: HashMap<&str, String>) -> ValidationError {
145    let registry = error_registry();
146    match registry.get(code) {
147        Some(entry) => ValidationError {
148            code: code.to_string(),
149            path: path.to_string(),
150            message: format_template(entry.message_template, &context),
151            severity: entry.severity.as_str().to_string(),
152            suggestion: None,
153        },
154        None => ValidationError {
155            code: code.to_string(),
156            path: path.to_string(),
157            message: format!("Unknown error code: {}", code),
158            severity: "error".to_string(),
159            suggestion: None,
160        },
161    }
162}