Skip to main content

elo_rust/runtime/
mod.rs

1//! Runtime support for validation
2//!
3//! Provides error types and utilities for generated validators
4
5use std::fmt;
6
7/// A single validation error
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ValidationError {
10    /// The path to the field being validated (e.g., "user.email")
11    pub path: String,
12    /// Human-readable error message
13    pub message: String,
14    /// The rule that failed
15    pub rule: String,
16    /// Optional value for debugging
17    pub value: Option<String>,
18}
19
20impl ValidationError {
21    /// Create a new validation error
22    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    /// Add a value for debugging
36    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/// Multiple validation errors
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct ValidationErrors {
53    /// Collection of validation errors
54    pub errors: Vec<ValidationError>,
55}
56
57impl ValidationErrors {
58    /// Create a new error collection
59    pub fn new() -> Self {
60        Self { errors: Vec::new() }
61    }
62
63    /// Add an error to the collection
64    pub fn push(&mut self, error: ValidationError) {
65        self.errors.push(error);
66    }
67
68    /// Check if there are any errors
69    pub fn is_empty(&self) -> bool {
70        self.errors.is_empty()
71    }
72
73    /// Get the number of errors
74    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}