dataflow_rs/engine/functions/
validation.rs

1use crate::engine::error::{DataflowError, Result};
2use serde::Deserialize;
3use serde_json::Value;
4
5/// Pre-parsed configuration for validation function
6#[derive(Debug, Clone, Deserialize)]
7pub struct ValidationConfig {
8    pub rules: Vec<ValidationRule>,
9}
10
11#[derive(Debug, Clone, Deserialize)]
12pub struct ValidationRule {
13    pub logic: Value,
14    pub path: String,
15    pub message: String,
16    #[serde(skip)]
17    pub logic_index: Option<usize>,
18}
19
20impl ValidationConfig {
21    pub fn from_json(input: &Value) -> Result<Self> {
22        let rules = input.get("rules").ok_or_else(|| {
23            DataflowError::Validation("Missing 'rules' array in input".to_string())
24        })?;
25
26        let rules_arr = rules
27            .as_array()
28            .ok_or_else(|| DataflowError::Validation("'rules' must be an array".to_string()))?;
29
30        let mut parsed_rules = Vec::new();
31
32        for rule in rules_arr {
33            let logic = rule
34                .get("logic")
35                .ok_or_else(|| DataflowError::Validation("Missing 'logic' in rule".to_string()))?
36                .clone();
37
38            let path = rule
39                .get("path")
40                .and_then(Value::as_str)
41                .unwrap_or("data")
42                .to_string();
43
44            let message = rule
45                .get("message")
46                .and_then(Value::as_str)
47                .unwrap_or("Validation failed")
48                .to_string();
49
50            parsed_rules.push(ValidationRule {
51                logic,
52                path,
53                message,
54                logic_index: None,
55            });
56        }
57
58        Ok(ValidationConfig {
59            rules: parsed_rules,
60        })
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use serde_json::json;
68
69    #[test]
70    fn test_validation_config_from_json() {
71        let input = json!({
72            "rules": [
73                {
74                    "logic": {"!!": [{"var": "data.required_field"}]},
75                    "path": "data",
76                    "message": "Required field is missing"
77                },
78                {
79                    "logic": {">": [{"var": "data.age"}, 18]},
80                    "message": "Must be over 18"
81                }
82            ]
83        });
84
85        let config = ValidationConfig::from_json(&input).unwrap();
86        assert_eq!(config.rules.len(), 2);
87        assert_eq!(config.rules[0].path, "data");
88        assert_eq!(config.rules[0].message, "Required field is missing");
89        assert_eq!(config.rules[1].path, "data"); // Default path
90        assert_eq!(config.rules[1].message, "Must be over 18");
91    }
92
93    #[test]
94    fn test_validation_config_missing_rules() {
95        let input = json!({});
96        let result = ValidationConfig::from_json(&input);
97        assert!(result.is_err());
98    }
99
100    #[test]
101    fn test_validation_config_invalid_rules() {
102        let input = json!({
103            "rules": "not_an_array"
104        });
105        let result = ValidationConfig::from_json(&input);
106        assert!(result.is_err());
107    }
108
109    #[test]
110    fn test_validation_config_missing_logic() {
111        let input = json!({
112            "rules": [
113                {
114                    "path": "data",
115                    "message": "Some error"
116                }
117            ]
118        });
119        let result = ValidationConfig::from_json(&input);
120        assert!(result.is_err());
121    }
122
123    #[test]
124    fn test_validation_config_defaults() {
125        let input = json!({
126            "rules": [
127                {
128                    "logic": {"var": "data.field"}
129                }
130            ]
131        });
132
133        let config = ValidationConfig::from_json(&input).unwrap();
134        assert_eq!(config.rules[0].path, "data");
135        assert_eq!(config.rules[0].message, "Validation failed");
136    }
137}