Skip to main content

elo_rust/runtime/
mod.rs

1//! Runtime support for validation
2//!
3//! Provides error types, utilities, and dynamic value representation for generated validators
4
5pub mod temporal;
6pub mod value;
7
8pub use temporal::TemporalValue;
9pub use value::EloValue;
10
11use std::fmt;
12
13/// A single validation error
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct ValidationError {
16    /// The path to the field being validated (e.g., "user.email")
17    pub path: String,
18    /// Human-readable error message
19    pub message: String,
20    /// The rule that failed
21    pub rule: String,
22    /// Optional value for debugging
23    pub value: Option<String>,
24}
25
26impl ValidationError {
27    /// Create a new validation error
28    pub fn new(
29        path: impl Into<String>,
30        message: impl Into<String>,
31        rule: impl Into<String>,
32    ) -> Self {
33        Self {
34            path: path.into(),
35            message: message.into(),
36            rule: rule.into(),
37            value: None,
38        }
39    }
40
41    /// Add a value for debugging
42    pub fn with_value(mut self, value: impl Into<String>) -> Self {
43        self.value = Some(value.into());
44        self
45    }
46}
47
48impl fmt::Display for ValidationError {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        write!(f, "{}: {}", self.path, self.message)
51    }
52}
53
54impl std::error::Error for ValidationError {}
55
56/// Multiple validation errors
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct ValidationErrors {
59    /// Collection of validation errors
60    pub errors: Vec<ValidationError>,
61}
62
63impl ValidationErrors {
64    /// Create a new error collection
65    pub fn new() -> Self {
66        Self { errors: Vec::new() }
67    }
68
69    /// Add an error to the collection
70    pub fn push(&mut self, error: ValidationError) {
71        self.errors.push(error);
72    }
73
74    /// Check if there are any errors
75    pub fn is_empty(&self) -> bool {
76        self.errors.is_empty()
77    }
78
79    /// Get the number of errors
80    pub fn len(&self) -> usize {
81        self.errors.len()
82    }
83}
84
85impl Default for ValidationErrors {
86    fn default() -> Self {
87        Self::new()
88    }
89}
90
91impl fmt::Display for ValidationErrors {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        for (i, err) in self.errors.iter().enumerate() {
94            if i > 0 {
95                writeln!(f)?;
96            }
97            write!(f, "{}", err)?;
98        }
99        Ok(())
100    }
101}
102
103impl std::error::Error for ValidationErrors {}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_validation_error_creation() {
111        let err = ValidationError::new("email", "Invalid email", "email_pattern");
112        assert_eq!(err.path, "email");
113        assert_eq!(err.message, "Invalid email");
114        assert_eq!(err.rule, "email_pattern");
115        assert_eq!(err.value, None);
116    }
117
118    #[test]
119    fn test_validation_error_with_value() {
120        let err =
121            ValidationError::new("email", "Invalid email", "email_pattern").with_value("invalid@");
122        assert_eq!(err.value, Some("invalid@".to_string()));
123    }
124
125    #[test]
126    fn test_validation_error_display() {
127        let err = ValidationError::new("email", "Invalid email", "email_pattern");
128        assert_eq!(err.to_string(), "email: Invalid email");
129    }
130
131    #[test]
132    fn test_validation_errors_collection() {
133        let mut errors = ValidationErrors::new();
134        assert!(errors.is_empty());
135        assert_eq!(errors.len(), 0);
136
137        errors.push(ValidationError::new("email", "Invalid", "rule1"));
138        assert!(!errors.is_empty());
139        assert_eq!(errors.len(), 1);
140
141        errors.push(ValidationError::new("age", "Too young", "rule2"));
142        assert_eq!(errors.len(), 2);
143    }
144}