bo4e_core/com/
validation_result.rs

1//! Validation result (Validierungsergebnis) component.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// Result of a validation check on measured data.
9///
10/// German: Validierungsergebnis
11///
12/// # Example
13///
14/// ```rust
15/// use bo4e_core::com::ValidationResult;
16/// use chrono::Utc;
17///
18/// let result = ValidationResult {
19///     validation_timestamp: Some(Utc::now()),
20///     is_valid: Some(true),
21///     ..Default::default()
22/// };
23/// ```
24#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct ValidationResult {
27    /// BO4E metadata
28    #[serde(flatten)]
29    pub meta: Bo4eMeta,
30
31    /// Timestamp of validation (Validierungszeitpunkt)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub validation_timestamp: Option<DateTime<Utc>>,
34
35    /// Whether validation passed (Gültig)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub is_valid: Option<bool>,
38
39    /// Validation rule ID (Validierungsregel)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub validation_rule_id: Option<String>,
42
43    /// Validation rule name (Regelbezeichnung)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub validation_rule_name: Option<String>,
46
47    /// Error code if validation failed (Fehlercode)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub error_code: Option<String>,
50
51    /// Error message (Fehlermeldung)
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub error_message: Option<String>,
54
55    /// Severity level (Schweregrad)
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub severity: Option<String>,
58}
59
60impl Bo4eObject for ValidationResult {
61    fn type_name_german() -> &'static str {
62        "Validierungsergebnis"
63    }
64
65    fn type_name_english() -> &'static str {
66        "ValidationResult"
67    }
68
69    fn meta(&self) -> &Bo4eMeta {
70        &self.meta
71    }
72
73    fn meta_mut(&mut self) -> &mut Bo4eMeta {
74        &mut self.meta
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use chrono::TimeZone;
82
83    #[test]
84    fn test_validation_passed() {
85        let result = ValidationResult {
86            validation_timestamp: Some(Utc.with_ymd_and_hms(2024, 1, 15, 12, 0, 0).unwrap()),
87            is_valid: Some(true),
88            validation_rule_id: Some("RULE_001".to_string()),
89            ..Default::default()
90        };
91
92        let json = serde_json::to_string(&result).unwrap();
93        assert!(json.contains("true"));
94        assert!(json.contains("RULE_001"));
95    }
96
97    #[test]
98    fn test_validation_failed() {
99        let result = ValidationResult {
100            validation_timestamp: Some(Utc::now()),
101            is_valid: Some(false),
102            error_code: Some("E001".to_string()),
103            error_message: Some("Value out of range".to_string()),
104            severity: Some("ERROR".to_string()),
105            ..Default::default()
106        };
107
108        let json = serde_json::to_string(&result).unwrap();
109        assert!(json.contains("false"));
110        assert!(json.contains("E001"));
111    }
112
113    #[test]
114    fn test_roundtrip() {
115        let result = ValidationResult {
116            is_valid: Some(true),
117            validation_rule_name: Some("Range check".to_string()),
118            ..Default::default()
119        };
120
121        let json = serde_json::to_string(&result).unwrap();
122        let parsed: ValidationResult = serde_json::from_str(&json).unwrap();
123        assert_eq!(result, parsed);
124    }
125
126    #[test]
127    fn test_bo4e_object_impl() {
128        assert_eq!(ValidationResult::type_name_german(), "Validierungsergebnis");
129        assert_eq!(ValidationResult::type_name_english(), "ValidationResult");
130    }
131}