swift_mt_message/validation/
mod.rs

1use crate::errors::{ParseError, Result};
2use datalogic_rs::DataLogic;
3use serde_json::Value;
4use std::fs;
5use std::path::Path;
6
7/// Validation result for a single rule
8#[derive(Debug)]
9pub struct ValidationResult {
10    pub rule_name: String,
11    pub passed: bool,
12    pub message: String,
13}
14
15/// Collection of validation results
16#[derive(Debug)]
17pub struct ValidationReport {
18    pub results: Vec<ValidationResult>,
19    pub overall_valid: bool,
20}
21
22impl Default for ValidationReport {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl ValidationReport {
29    /// Create a new empty validation report
30    pub fn new() -> Self {
31        Self {
32            results: Vec::new(),
33            overall_valid: true,
34        }
35    }
36
37    /// Add a validation result
38    pub fn add_result(&mut self, result: ValidationResult) {
39        if !result.passed {
40            self.overall_valid = false;
41        }
42        self.results.push(result);
43    }
44
45    /// Get the number of failed validations
46    pub fn failure_count(&self) -> usize {
47        self.results.iter().filter(|r| !r.passed).count()
48    }
49
50    /// Get all failed validation results
51    pub fn get_failures(&self) -> Vec<&ValidationResult> {
52        self.results.iter().filter(|r| !r.passed).collect()
53    }
54}
55
56/// Validates an MT message using JSONLogic rules from a file
57///
58/// # Arguments
59/// * `message_json` - The MT message converted to JSON Value
60/// * `rules_file_path` - Path to the JSON file containing validation rules
61///
62/// # Returns
63/// * `ValidationReport` containing detailed validation results
64pub fn validate_mt_message_with_rules<P: AsRef<Path>>(
65    message_json: Value,
66    rules_file_path: P,
67) -> Result<ValidationReport> {
68    let mut report = ValidationReport::new();
69
70    // Read the rules file
71    let rules_content =
72        fs::read_to_string(rules_file_path).map_err(|e| ParseError::ValidationError {
73            message: format!("Failed to read rules file: {}", e),
74        })?;
75
76    // Parse rules JSON
77    let rules_json: Value =
78        serde_json::from_str(&rules_content).map_err(|e| ParseError::ValidationError {
79            message: format!("Failed to parse rules JSON: {}", e),
80        })?;
81
82    // Extract rules array
83    let rules_array = rules_json
84        .get("rules")
85        .and_then(|r| r.as_array())
86        .ok_or_else(|| ParseError::ValidationError {
87            message: "Rules file must contain a 'rules' array".to_string(),
88        })?;
89
90    // Create DataLogic engine
91    let engine = DataLogic::new();
92
93    // Process each rule
94    for rule in rules_array {
95        let rule_name = rule
96            .get("name")
97            .and_then(|n| n.as_str())
98            .unwrap_or("Unknown Rule")
99            .to_string();
100
101        let rule_message = rule
102            .get("message")
103            .and_then(|m| m.as_str())
104            .unwrap_or("Validation failed")
105            .to_string();
106
107        // Extract logic from rule
108        let logic = rule
109            .get("logic")
110            .ok_or_else(|| ParseError::ValidationError {
111                message: format!("Rule '{}' missing 'logic' field", rule_name),
112            })?;
113
114        // Evaluate the rule
115        let rule_result = engine
116            .evaluate_json(logic, &message_json, None)
117            .map_err(|e| ParseError::ValidationError {
118                message: format!("Failed to evaluate rule '{}': {}", rule_name, e),
119            })?;
120
121        // Convert result to boolean
122        let passed = rule_result.is_boolean() && rule_result.as_bool().unwrap();
123
124        report.add_result(ValidationResult {
125            rule_name,
126            passed,
127            message: rule_message,
128        });
129    }
130
131    Ok(report)
132}