diff --git a/Cargo.lock b/Cargo.lock index c4a116b..c3e0fb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1487,7 +1487,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1794,9 +1794,9 @@ dependencies = [ [[package]] name = "tucana" -version = "0.0.52" +version = "0.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e1cec14d8140417b330fffd1fb07f52cbd69063a4bd5688e63511b5bf8c8aa" +checksum = "31e81f5598a25b6a8f1618fcb25d1a8b2ddbfa38c8a9dcba16a798efe425a36e" dependencies = [ "pbjson", "pbjson-build", @@ -1917,7 +1917,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 50ece43..64931e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://code0.tech" license = "Apache-2.0" [dependencies] -tucana = { version = "0.0.52", features = ["aquila"] } +tucana = { version = "0.0.54", features = ["aquila"] } async-trait = "0.1.85" log = "0.4.24" tonic = "0.14.1" @@ -31,5 +31,4 @@ flow_definition = [] flow_service = ["flow_definition"] flow_config = [] flow_health = [] -flow_validator = [] -all = ["flow_definition", "flow_config", "flow_health", "flow_validator", "flow_service"] +all = ["flow_definition", "flow_config", "flow_health", "flow_service"] diff --git a/src/flow_validator/mod.rs b/src/flow_validator/mod.rs deleted file mode 100644 index a2dac68..0000000 --- a/src/flow_validator/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -mod rule; - -use rule::{ - contains_key::apply_contains_key, - contains_type::apply_contains_type, - item_of_collection::apply_item_of_collection, - number_range::apply_number_range, - regex::apply_regex, - violation::{DataTypeNotFoundRuleViolation, DataTypeRuleError, DataTypeRuleViolation}, -}; - -use tucana::shared::{ExecutionDataType, ValidationFlow, Value, execution_data_type_rule::Config}; -pub struct VerificationResult; - -pub fn verify_flow(flow: ValidationFlow, body: Value) -> Result<(), DataTypeRuleError> { - let input_type = match &flow.input_type_identifier { - Some(r) => r.clone(), - None => return Ok(()), //Returns directly because no rule is given. The body is ok and will not be concidered - }; - - let data_type = match flow - .data_types - .iter() - .find(|dt| dt.identifier == input_type) - { - Some(dt) => dt.clone(), - None => { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::DataTypeNotFound( - DataTypeNotFoundRuleViolation { - data_type: input_type, - }, - )], - }); - } - }; - - verify_data_type_rules(body, data_type, &flow.data_types) -} - -//Verifies the rules on the datatype of the body thats given -fn verify_data_type_rules( - body: Value, - data_type: ExecutionDataType, - availabe_data_types: &Vec, -) -> Result<(), DataTypeRuleError> { - let mut violations: Vec = Vec::new(); - for rule in data_type.rules { - let rule_config = match rule.config { - None => continue, - Some(config) => config, - }; - - match rule_config { - Config::NumberRange(config) => { - match apply_number_range(config, &body, &String::from("value")) { - Ok(_) => continue, - Err(violation) => { - violations.extend(violation.violations); - continue; - } - } - } - Config::ItemOfCollection(config) => { - match apply_item_of_collection(config, &body, "key") { - Ok(_) => continue, - Err(violation) => { - violations.extend(violation.violations); - continue; - } - } - } - Config::ContainsType(config) => { - match apply_contains_type(config, availabe_data_types, &body) { - Ok(_) => continue, - Err(violation) => { - violations.extend(violation.violations); - continue; - } - } - } - Config::Regex(config) => { - match apply_regex(config, &body) { - Ok(_) => continue, - Err(violation) => { - violations.extend(violation.violations); - continue; - } - }; - } - Config::ContainsKey(config) => { - match apply_contains_key(config, &body, availabe_data_types) { - Ok(_) => continue, - Err(violation) => { - violations.extend(violation.violations); - continue; - } - }; - } - } - } - - if violations.is_empty() { - Ok(()) - } else { - Err(DataTypeRuleError { violations }) - } -} - -fn get_data_type_by_id( - data_types: &Vec, - identifier: &String, -) -> Option { - data_types - .iter() - .find(|data_type| &data_type.identifier == identifier) - .cloned() -} diff --git a/src/flow_validator/rule/contains_key.rs b/src/flow_validator/rule/contains_key.rs deleted file mode 100644 index 1cb9b24..0000000 --- a/src/flow_validator/rule/contains_key.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::violation::ContainsKeyRuleViolation; -use super::violation::DataTypeRuleError; -use super::violation::DataTypeRuleViolation; -use super::violation::MissingDataTypeRuleDefinition; -use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules}; -use tucana::shared::ExecutionDataType; -use tucana::shared::ExecutionDataTypeContainsKeyRuleConfig; -use tucana::shared::Value; -use tucana::shared::helper::path::expect_kind; -use tucana::shared::value::Kind; - -/// # Data Type Validation Behavior -/// -/// This function checks if a specific key exists in the JSON body and validates -/// if its value matches the expected data type. -/// -/// ## Process: -/// 1. Searches for the specified key in the JSON body -/// 2. If the key is found, retrieves the associated data type definition from the flow -/// 3. Validates that the value matches the expected data type -/// -/// ## Error Handling: -/// - Returns a `ContainsKeyRuleViolation` if the specified key is not found in the body -/// - Returns a `MissingDataTypeRuleDefinition` if the referenced data type doesn't exist -/// - Returns validation errors if the value doesn't match the expected data type -pub fn apply_contains_key( - rule: ExecutionDataTypeContainsKeyRuleConfig, - body: &Value, - available_data_types: &Vec, -) -> Result<(), DataTypeRuleError> { - let identifier = rule.data_type_identifier; - - if let Some(Kind::StructValue(_)) = &body.kind { - let value = match expect_kind(&identifier, body) { - Some(value) => Value { - kind: Some(value.to_owned()), - }, - None => { - let error = ContainsKeyRuleViolation { - missing_key: identifier, - }; - - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::ContainsKey(error)], - }); - } - }; - - let data_type = match get_data_type_by_id(available_data_types, &identifier) { - Some(data_type) => data_type, - None => { - let error = MissingDataTypeRuleDefinition { - missing_type: identifier, - }; - - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::MissingDataType(error)], - }); - } - }; - - verify_data_type_rules(value, data_type, available_data_types) - } else { - Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::ContainsKey( - ContainsKeyRuleViolation { - missing_key: identifier, - }, - )], - }) - } -} diff --git a/src/flow_validator/rule/contains_type.rs b/src/flow_validator/rule/contains_type.rs deleted file mode 100644 index 97a00ff..0000000 --- a/src/flow_validator/rule/contains_type.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::violation::{DataTypeRuleError, DataTypeRuleViolation, InvalidFormatRuleViolation}; -use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules}; -use tucana::shared::{ - ExecutionDataType, ExecutionDataTypeContainsTypeRuleConfig, Value, value::Kind, -}; - -/// # Item of Collection Validation -/// -/// This function validates if a value is contained within a predefined collection of allowed items. -/// -/// ## Process: -/// 1. Checks if the provided value is present in the collection of allowed items -/// -/// ## Error Handling: -/// - Returns an `ItemOfCollectionRuleViolation` if the value is not found in the collection -/// -pub fn apply_contains_type( - rule: ExecutionDataTypeContainsTypeRuleConfig, - available_data_types: &Vec, - body: &Value, -) -> Result<(), DataTypeRuleError> { - let identifier = rule.data_type_identifier; - let real_body = match &body.kind { - Some(body) => body.clone(), - None => { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::InvalidFormat( - InvalidFormatRuleViolation { - expected_format: identifier, - value: String::from("other"), - }, - )], - }); - } - }; - - match real_body { - Kind::ListValue(list) => { - let real_data_type = get_data_type_by_id(available_data_types, &identifier); - - if let Some(data_type) = real_data_type { - let mut rule_errors: Option = None; - - for value in list.values { - match verify_data_type_rules(value, data_type.clone(), available_data_types) { - Ok(_) => {} - Err(errors) => { - rule_errors = Some(errors); - } - } - } - - if let Some(errors) = rule_errors { - return Err(errors); - } else { - return Ok(()); - } - } - } - _ => { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::InvalidFormat( - InvalidFormatRuleViolation { - expected_format: identifier, - value: String::from("other"), - }, - )], - }); - } - } - - Ok(()) -} diff --git a/src/flow_validator/rule/item_of_collection.rs b/src/flow_validator/rule/item_of_collection.rs deleted file mode 100644 index 2aa48f5..0000000 --- a/src/flow_validator/rule/item_of_collection.rs +++ /dev/null @@ -1,187 +0,0 @@ -use super::violation::{DataTypeRuleError, DataTypeRuleViolation, ItemOfCollectionRuleViolation}; -use tucana::shared::{DataTypeItemOfCollectionRuleConfig, Value}; - -/// # Item of Collection Validation -/// -/// This function validates if a value is contained within a predefined collection of allowed items. -/// -/// ## Process: -/// 1. Checks if the provided value is present in the collection of allowed items -/// -/// ## Error Handling: -/// - Returns an `ItemOfCollectionRuleViolation` if the value is not found in the collection -/// -pub fn apply_item_of_collection( - rule: DataTypeItemOfCollectionRuleConfig, - body: &Value, - key: &str, -) -> Result<(), DataTypeRuleError> { - if !rule.items.contains(body) { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::ItemOfCollection( - ItemOfCollectionRuleViolation { - collection_name: String::from(key), - }, - )], - }); - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashMap; - use tucana::shared::{ListValue, NullValue, Struct, Value}; - - #[test] - fn test_apply_item_of_collection_success() { - let value = Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "allowed_value".to_string(), - )), - }; - let items = vec![ - Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "allowed_value".to_string(), - )), - }, - Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "another_allowed_value".to_string(), - )), - }, - ]; - - let rule = DataTypeItemOfCollectionRuleConfig { items }; - let result = apply_item_of_collection(rule, &value, "test_field"); - - assert!(result.is_ok()); - } - - #[test] - fn test_apply_item_of_collection_failure() { - let value = Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "disallowed_value".to_string(), - )), - }; - let items = vec![ - Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "allowed_value".to_string(), - )), - }, - Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "another_allowed_value".to_string(), - )), - }, - ]; - - let rule = DataTypeItemOfCollectionRuleConfig { items }; - let result = apply_item_of_collection(rule, &value, "test_field"); - - assert!(result.is_err()); - if let Err(error) = result { - assert_eq!(error.violations.len(), 1); - match &error.violations[0] { - DataTypeRuleViolation::ItemOfCollection(violation) => { - assert_eq!(violation.collection_name, "test_field"); - } - _ => panic!("Expected ItemOfCollection violation"), - } - } - } - - #[test] - fn test_apply_item_of_collection_with_different_value_types() { - let value = Value { - kind: Some(tucana::shared::value::Kind::NumberValue(42.0)), - }; - let items = vec![ - Value { - kind: Some(tucana::shared::value::Kind::StringValue( - "allowed_value".to_string(), - )), - }, - Value { - kind: Some(tucana::shared::value::Kind::NumberValue(42.0)), - }, - ]; - - let rule = DataTypeItemOfCollectionRuleConfig { items }; - let result = apply_item_of_collection(rule, &value, "test_field"); - - assert!(result.is_ok()); - } - - #[test] - fn test_apply_item_of_collection_with_complex_values() { - let mut fields = HashMap::new(); - fields.insert( - "name".to_string(), - Value { - kind: Some(tucana::shared::value::Kind::StringValue("test".to_string())), - }, - ); - fields.insert( - "age".to_string(), - Value { - kind: Some(tucana::shared::value::Kind::NumberValue(30.0)), - }, - ); - let struct_value = Value { - kind: Some(tucana::shared::value::Kind::StructValue(Struct { fields })), - }; - - let list_values = vec![ - Value { - kind: Some(tucana::shared::value::Kind::StringValue("one".to_string())), - }, - Value { - kind: Some(tucana::shared::value::Kind::NumberValue(2.0)), - }, - ]; - let list_value = Value { - kind: Some(tucana::shared::value::Kind::ListValue(ListValue { - values: list_values, - })), - }; - - let null_value = Value { - kind: Some(tucana::shared::value::Kind::NullValue( - NullValue::NullValue as i32, - )), - }; - - let items = vec![struct_value.clone(), list_value.clone(), null_value.clone()]; - let rule = DataTypeItemOfCollectionRuleConfig { items }; - - assert!(apply_item_of_collection(rule.clone(), &struct_value, "test_field").is_ok()); - assert!(apply_item_of_collection(rule.clone(), &list_value, "test_field").is_ok()); - assert!(apply_item_of_collection(rule, &null_value, "test_field").is_ok()); - - let mut different_fields = HashMap::new(); - different_fields.insert( - "different".to_string(), - Value { - kind: Some(tucana::shared::value::Kind::BoolValue(true)), - }, - ); - let different_struct = Value { - kind: Some(tucana::shared::value::Kind::StructValue(Struct { - fields: different_fields, - })), - }; - - let rule_for_failure = DataTypeItemOfCollectionRuleConfig { - items: vec![struct_value], - }; - assert!( - apply_item_of_collection(rule_for_failure, &different_struct, "test_field").is_err() - ); - } -} diff --git a/src/flow_validator/rule/mod.rs b/src/flow_validator/rule/mod.rs deleted file mode 100644 index f39a8a0..0000000 --- a/src/flow_validator/rule/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod contains_key; -pub mod contains_type; -pub mod item_of_collection; -pub mod number_range; -pub mod regex; -pub mod violation; diff --git a/src/flow_validator/rule/number_range.rs b/src/flow_validator/rule/number_range.rs deleted file mode 100644 index e9b28b2..0000000 --- a/src/flow_validator/rule/number_range.rs +++ /dev/null @@ -1,134 +0,0 @@ -use tucana::shared::{DataTypeNumberRangeRuleConfig, Value, value::Kind}; - -use super::violation::{ - DataTypeRuleError, DataTypeRuleViolation, NumberInRangeRuleViolation, - RegexRuleTypeNotAcceptedViolation, -}; - -/// # Number Range Validation -/// -/// This function validates if a numeric value falls within a specified range and follows step constraints. -/// -/// ## Process: -/// 1. Extracts the numeric value from the input (if it is a number) -/// 2. Checks if the number is within the specified range (from/to) -/// 3. If steps are specified, verifies the number is divisible by the step value -/// -/// ## Error Handling: -/// - Returns a `RegexRuleTypeNotAcceptedViolation` if the value is not a number -/// - Returns a `NumberInRangeRuleViolation` if the number is outside the specified range -/// - Returns a `NumberInRangeRuleViolation` if the number doesn't conform to the step constraint -/// -pub fn apply_number_range( - rule: DataTypeNumberRangeRuleConfig, - body: &Value, - key: &str, -) -> Result<(), DataTypeRuleError> { - let kind = match &body.kind { - Some(kind) => kind, - None => return Ok(()), - }; - - let result = match kind { - Kind::NumberValue(n) => *n, - _ => { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::RegexTypeNotAccepted( - RegexRuleTypeNotAcceptedViolation { - type_not_accepted: format!("{:?}", kind), - }, - )], - }); - } - }; - - if result < rule.from as f64 || result > rule.to as f64 { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::NumberInRange( - NumberInRangeRuleViolation { - key: String::from(key), - }, - )], - }); - } - - if let Some(modulo) = rule.steps { - if modulo == 0 { - return Ok(()); - } - - if result % modulo as f64 != 0.0 { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::NumberInRange( - NumberInRangeRuleViolation { - key: String::from(key), - }, - )], - }); - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn number_as_value(number: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(number)), - } - } - - #[test] - fn test_apply_number_range() { - let rule = DataTypeNumberRangeRuleConfig { - from: 1, - to: 10, - steps: None, - }; - - assert!(apply_number_range(rule, &number_as_value(-2.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(2.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(3.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(11.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(12.0), "test").is_err()); - } - - #[test] - fn test_apply_number_range_with_steps() { - let rule = DataTypeNumberRangeRuleConfig { - from: 1, - to: 10, - steps: Some(2), - }; - - assert!(apply_number_range(rule, &number_as_value(2.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(4.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(6.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(8.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(10.0), "test").is_ok()); - assert!(apply_number_range(rule, &number_as_value(1.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(3.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(5.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(7.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(9.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(11.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(12.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(-12.0), "test").is_err()); - } - - #[test] - fn test_apply_number_range_with_falty_steps() { - let rule = DataTypeNumberRangeRuleConfig { - from: 1, - to: 10, - steps: Some(0), - }; - - assert!(apply_number_range(rule, &number_as_value(-12.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(12.0), "test").is_err()); - assert!(apply_number_range(rule, &number_as_value(6.0), "test").is_ok()); - } -} diff --git a/src/flow_validator/rule/regex.rs b/src/flow_validator/rule/regex.rs deleted file mode 100644 index fb15672..0000000 --- a/src/flow_validator/rule/regex.rs +++ /dev/null @@ -1,245 +0,0 @@ -use super::violation::{ - DataTypeRuleError, DataTypeRuleViolation, RegexRuleTypeNotAcceptedViolation, RegexRuleViolation, -}; -use tucana::shared::{DataTypeRegexRuleConfig, Value, value::Kind}; - -/// # Regex Pattern Validation -/// -/// This function validates if a value matches a specified regex pattern. -/// -/// ## Process: -/// 1. Converts the input value to a string representation (if possible) -/// 2. Compiles the regex pattern from the rule -/// 3. Checks if the string representation matches the regex pattern -/// -/// ## Error Handling: -/// - Returns a `RegexRuleTypeNotAcceptedViolation` if the value type cannot be converted to a string -/// (e.g., arrays, objects) -/// - Returns a `RegexRuleViolation` if the string representation does not match the specified pattern -/// -pub fn apply_regex(rule: DataTypeRegexRuleConfig, body: &Value) -> Result<(), DataTypeRuleError> { - let kind = match &body.kind { - Some(kind) => kind, - None => return Ok(()), - }; - - let result = match kind { - Kind::BoolValue(b) => b.to_string(), - Kind::NumberValue(n) => n.to_string(), - Kind::StringValue(s) => s.clone(), - Kind::NullValue(_) => "null".to_string(), - Kind::StructValue(s) => { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::RegexTypeNotAccepted( - RegexRuleTypeNotAcceptedViolation { - type_not_accepted: format!("StructValue({:?})", s), - }, - )], - }); - } - Kind::ListValue(l) => { - return Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::RegexTypeNotAccepted( - RegexRuleTypeNotAcceptedViolation { - type_not_accepted: format!("ListValue({:?})", l), - }, - )], - }); - } - }; - - let regex = regex::Regex::new(rule.pattern.as_str()).unwrap(); - - if !regex.is_match(&result) { - Err(DataTypeRuleError { - violations: vec![DataTypeRuleViolation::Regex(RegexRuleViolation { - missing_regex: rule.pattern.clone(), - })], - }) - } else { - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use tucana::shared::{ListValue, Struct}; - - #[test] - fn test_apply_regex_with_matching_string() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from("^[a-z]+$"), - }; - let value = Value { - kind: Some(Kind::StringValue(String::from("abcde"))), - }; - - assert!(apply_regex(rule, &value).is_ok()); - } - - #[test] - fn test_apply_regex_with_non_matching_string() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from("^[a-z]+$"), - }; - let value = Value { - kind: Some(Kind::StringValue(String::from("123"))), - }; - - let result = apply_regex(rule, &value); - assert!(result.is_err()); - - if let Err(DataTypeRuleError { violations }) = result { - assert_eq!(violations.len(), 1); - match &violations[0] { - DataTypeRuleViolation::Regex(violation) => { - assert_eq!(violation.missing_regex, "^[a-z]+$"); - } - _ => panic!("Expected RegexRuleViolation"), - } - } - } - - #[test] - fn test_apply_regex_with_matching_boolean() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from("^true$"), - }; - let value = Value { - kind: Some(Kind::BoolValue(true)), - }; - - assert!(apply_regex(rule, &value).is_ok()); - } - - #[test] - fn test_apply_regex_with_non_matching_boolean() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from("^false$"), - }; - let value = Value { - kind: Some(Kind::BoolValue(true)), - }; - - assert!(apply_regex(rule, &value).is_err()); - } - - #[test] - fn test_apply_regex_with_matching_number() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from("^42$"), - }; - let value = Value { - kind: Some(Kind::NumberValue(42.0)), - }; - - assert!(apply_regex(rule, &value).is_ok()); - } - - #[test] - fn test_apply_regex_with_non_matching_number() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from("^[0-9]+$"), - }; - let value = Value { - kind: Some(Kind::NumberValue(3.14)), - }; - - assert!(apply_regex(rule, &value).is_err()); - } - - #[test] - fn test_apply_regex_with_array() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from(".*"), - }; - let value = Value { - kind: Some(Kind::ListValue(ListValue { values: vec![] })), - }; - - let result = apply_regex(rule, &value); - assert!(result.is_err()); - - if let Err(DataTypeRuleError { violations }) = result { - assert_eq!(violations.len(), 1); - match &violations[0] { - DataTypeRuleViolation::RegexTypeNotAccepted(violation) => { - assert!(violation.type_not_accepted.contains("ListValue")); - } - _ => panic!("Expected RegexRuleTypeNotAcceptedViolation"), - } - } - } - - #[test] - fn test_apply_regex_with_object() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from(".*"), - }; - let value = Value { - kind: Some(Kind::StructValue(Struct { - fields: Default::default(), - })), - }; - - let result = apply_regex(rule, &value); - assert!(result.is_err()); - - if let Err(DataTypeRuleError { violations }) = result { - assert_eq!(violations.len(), 1); - match &violations[0] { - DataTypeRuleViolation::RegexTypeNotAccepted(violation) => { - assert!(violation.type_not_accepted.contains("StructValue")); - } - _ => panic!("Expected RegexRuleTypeNotAcceptedViolation"), - } - } - } - - #[test] - fn test_apply_regex_with_null_kind() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from(".*"), - }; - let value = Value { kind: None }; - - assert!(apply_regex(rule, &value).is_ok()); - } - - #[test] - fn test_apply_regex_complex_pattern() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from(r"^\d{3}-\d{2}-\d{4}$"), // SSN pattern - }; - let value = Value { - kind: Some(Kind::StringValue(String::from("123-45-6789"))), - }; - - assert!(apply_regex(rule.clone(), &value).is_ok()); - - let invalid_value = Value { - kind: Some(Kind::StringValue(String::from("123-456-789"))), - }; - - assert!(apply_regex(rule, &invalid_value).is_err()); - } - - #[test] - fn test_apply_regex_email_pattern() { - let rule = DataTypeRegexRuleConfig { - pattern: String::from(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"), - }; - let value = Value { - kind: Some(Kind::StringValue(String::from("test@example.com"))), - }; - - assert!(apply_regex(rule.clone(), &value).is_ok()); - - let invalid_value = Value { - kind: Some(Kind::StringValue(String::from("invalid-email"))), - }; - - assert!(apply_regex(rule, &invalid_value).is_err()); - } -} diff --git a/src/flow_validator/rule/violation.rs b/src/flow_validator/rule/violation.rs deleted file mode 100644 index 4257e89..0000000 --- a/src/flow_validator/rule/violation.rs +++ /dev/null @@ -1,166 +0,0 @@ -pub struct DataTypeRuleError { - pub violations: Vec, -} - -pub enum DataTypeRuleViolation { - MissingDataType(MissingDataTypeRuleDefinition), - ContainsKey(ContainsKeyRuleViolation), - Regex(RegexRuleViolation), - RegexTypeNotAccepted(RegexRuleTypeNotAcceptedViolation), - DataTypeNotFound(DataTypeNotFoundRuleViolation), - NumberInRange(NumberInRangeRuleViolation), - ItemOfCollection(ItemOfCollectionRuleViolation), - InvalidFormat(InvalidFormatRuleViolation), - GenericKeyNotAllowed(GenericKeyNotAllowedRuleViolation), - DataTypeIdentifierNotPresent(DataTypeIdentifierNotPresentRuleViolation), -} - -pub struct MissingDataTypeRuleDefinition { - pub missing_type: String, -} - -pub struct ContainsKeyRuleViolation { - pub missing_key: String, -} - -pub struct RegexRuleViolation { - pub missing_regex: String, -} - -pub struct RegexRuleTypeNotAcceptedViolation { - pub type_not_accepted: String, -} - -pub struct DataTypeNotFoundRuleViolation { - pub data_type: String, -} - -pub struct NumberInRangeRuleViolation { - pub key: String, -} - -pub struct ItemOfCollectionRuleViolation { - pub collection_name: String, -} - -pub struct InvalidFormatRuleViolation { - pub expected_format: String, - pub value: String, -} - -pub struct GenericKeyNotAllowedRuleViolation { - pub key: String, -} - -pub struct DataTypeIdentifierNotPresentRuleViolation { - pub identifier: String, -} - -impl DataTypeRuleError { - pub fn to_string(&self) -> String { - let mut violations = Vec::new(); - - for violation in &self.violations { - match violation { - DataTypeRuleViolation::ContainsKey(v) => { - violations.push(serde_json::json!({ - "type": "ContainsKey", - "explanation": format!("Missing required key: '{}'", v.missing_key), - "details": { - "missing_key": v.missing_key - } - })); - } - DataTypeRuleViolation::Regex(v) => { - violations.push(serde_json::json!({ - "type": "Regex", - "explanation": format!("Failed to match regex pattern: '{}'", v.missing_regex), - "details": { - "missing_regex": v.missing_regex - } - })); - } - DataTypeRuleViolation::MissingDataType(v) => { - violations.push(serde_json::json!({ - "type": "MissingDataType", - "explanation": format!("Missing required data type: '{}'", v.missing_type), - "details": { - "missing_type": v.missing_type - } - })); - } - DataTypeRuleViolation::RegexTypeNotAccepted(v) => { - violations.push(serde_json::json!({ - "type": "RegexTypeNotAccepted", - "explanation": format!("Regex pattern does not match data type: '{}'", v.type_not_accepted), - "details": { - "type_not_accepted": v.type_not_accepted - } - })); - } - DataTypeRuleViolation::DataTypeNotFound(v) => { - violations.push(serde_json::json!({ - "type": "DataTypeNotFound", - "explanation": format!("Data type not found: '{}'", v.data_type), - "details": { - "data_type": v.data_type - } - })); - } - DataTypeRuleViolation::NumberInRange(v) => { - violations.push(serde_json::json!({ - "type": "NumberInRange", - "explanation": format!("Number not in valid range for key: '{}'", v.key), - "details": { - "key": v.key - } - })); - } - DataTypeRuleViolation::ItemOfCollection(v) => { - violations.push(serde_json::json!({ - "type": "ItemOfCollection", - "explanation": format!("Item is not a valid member of collection: '{}'", v.collection_name), - "details": { - "collection_name": v.collection_name - } - })); - } - DataTypeRuleViolation::InvalidFormat(v) => { - violations.push(serde_json::json!({ - "type": "InvalidFormat", - "explanation": format!("Invalid format. Expected: '{}', Got: '{}'", v.expected_format, v.value), - "details": { - "expected_format": v.expected_format, - "value": v.value - } - })); - } - DataTypeRuleViolation::GenericKeyNotAllowed(v) => { - violations.push(serde_json::json!({ - "type": "GenericKeyNotAllowed", - "explanation": format!("Generic key not allowed: '{}'", v.key), - "details": { - "key": v.key - } - })); - } - DataTypeRuleViolation::DataTypeIdentifierNotPresent(v) => { - violations.push(serde_json::json!({ - "type": "DataTypeIdentifierNotPresent", - "explanation": format!("Data type identifier not present: '{}'", v.identifier), - "details": { - "identifier": v.identifier - } - })); - } - } - } - - serde_json::json!({ - "error": "DataTypeRuleError", - "violation_count": self.violations.len(), - "violations": violations - }) - .to_string() - } -} diff --git a/src/lib.rs b/src/lib.rs index d857809..dd792e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,5 @@ pub mod flow_config; #[cfg(feature = "flow_health")] pub mod flow_health; -#[cfg(feature = "flow_validator")] -pub mod flow_validator; - #[cfg(feature = "flow_service")] pub mod flow_service;