diff --git a/Cargo.lock b/Cargo.lock index 82616f4..0bbf67e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,32 +212,22 @@ dependencies = [ "num-traits", ] -[[package]] -name = "code0-definition-reader" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e3a3aec6ffc35ac360113d8b96206c38961afef56b0c5632cb77cd2f4429b9" -dependencies = [ - "serde", - "serde_json", - "tucana", -] - [[package]] name = "code0-flow" version = "0.0.0" dependencies = [ "async-nats", "async-trait", - "code0-definition-reader", "dotenv", "futures-core", "log", "regex", + "serde", "serde_json", "tonic", "tonic-health", "tucana", + "walkdir", ] [[package]] @@ -1342,6 +1332,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.27" @@ -1382,9 +1381,9 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.221" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341877e04a22458705eb4e131a1508483c877dca2792b3781d4e5d8a6019ec43" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -1392,18 +1391,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.221" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c459bc0a14c840cb403fc14b148620de1e0778c96ecd6e0c8c3cacb6d8d00fe" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.221" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6185cf75117e20e62b1ff867b9518577271e58abe0037c40bb4794969355ab0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1874,9 +1873,9 @@ dependencies = [ [[package]] name = "tucana" -version = "0.0.39" +version = "0.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6300e0f85e9352904cace7b285366a10995dbf690c6ed8fa022cd57fa8607b" +checksum = "22ab56226ccbbda9b2bd7505d4296712a2e0757254fbe601c30785ae9e2d09e6" dependencies = [ "pbjson", "pbjson-build", @@ -1939,6 +1938,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -1981,6 +1990,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.60.2", +] + [[package]] name = "windows-link" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 317f1c9..9ec1eca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,17 +8,18 @@ homepage = "https://code0.tech" license = "Apache-2.0" [dependencies] -tucana = { version = "0.0.39", features = ["aquila"] } +tucana = { version = "0.0.42", features = ["aquila"] } async-trait = "0.1.85" log = "0.4.24" tonic = "0.14.1" dotenv = "0.15.0" -code0-definition-reader = "0.0.18" tonic-health = "0.14.1" async-nats = "0.45.0" futures-core = "0.3.31" regex = "1.11.2" serde_json = "1.0.143" +walkdir = "2.5.0" +serde = "1.0.228" [lib] doctest = true @@ -26,7 +27,8 @@ doctest = true [features] default = ["all"] flow_definition = [] +flow_service = ["flow_definition"] flow_config = [] flow_health = [] flow_validator = [] -all = ["flow_definition", "flow_config", "flow_health", "flow_validator"] +all = ["flow_definition", "flow_config", "flow_health", "flow_validator", "flow_service"] diff --git a/src/flow_definition/error/mod.rs b/src/flow_definition/error/mod.rs new file mode 100644 index 0000000..0eeb34e --- /dev/null +++ b/src/flow_definition/error/mod.rs @@ -0,0 +1,23 @@ +use std::io; +use std::path::PathBuf; + +#[derive(Debug)] +pub enum ReaderError { + JsonError { + path: PathBuf, + error: serde_json::Error, + }, + ReadFeatureError { + path: String, + source: Box, + }, + ReadDirectoryError { + path: PathBuf, + error: io::Error, + }, + ReadFileError { + path: PathBuf, + error: io::Error, + }, + DirectoryEntryError(io::Error), +} diff --git a/src/flow_definition/feature/mod.rs b/src/flow_definition/feature/mod.rs new file mode 100644 index 0000000..faec114 --- /dev/null +++ b/src/flow_definition/feature/mod.rs @@ -0,0 +1,12 @@ +pub mod version; + +use serde::Deserialize; +use tucana::shared::{DefinitionDataType, FlowType, RuntimeFunctionDefinition}; + +#[derive(Deserialize, Debug, Clone)] +pub struct Feature { + pub name: String, + pub data_types: Vec, + pub flow_types: Vec, + pub functions: Vec, +} diff --git a/src/flow_definition/feature/version.rs b/src/flow_definition/feature/version.rs new file mode 100644 index 0000000..ef1c098 --- /dev/null +++ b/src/flow_definition/feature/version.rs @@ -0,0 +1,50 @@ +use tucana::shared::{DefinitionDataType, FlowType, RuntimeFunctionDefinition, Version}; + +pub trait HasVersion { + fn version(&self) -> &Option; + fn version_mut(&mut self) -> &mut Option; + + fn normalize_version(&mut self) { + self.version_mut().get_or_insert(Version { + major: 0, + minor: 0, + patch: 0, + }); + } + + fn is_accepted(&self, filter: &Option) -> bool { + filter + .as_ref() + .is_none_or(|v| self.version().as_ref() == Some(v)) + } +} + +impl HasVersion for DefinitionDataType { + fn version(&self) -> &Option { + &self.version + } + + fn version_mut(&mut self) -> &mut Option { + &mut self.version + } +} + +impl HasVersion for FlowType { + fn version(&self) -> &Option { + &self.version + } + + fn version_mut(&mut self) -> &mut Option { + &mut self.version + } +} + +impl HasVersion for RuntimeFunctionDefinition { + fn version(&self) -> &Option { + &self.version + } + + fn version_mut(&mut self) -> &mut Option { + &mut self.version + } +} diff --git a/src/flow_definition/mod.rs b/src/flow_definition/mod.rs index dca95d1..5f503c6 100644 --- a/src/flow_definition/mod.rs +++ b/src/flow_definition/mod.rs @@ -1,187 +1,237 @@ -use tucana::{ - aquila::{ - DataTypeUpdateRequest, FlowTypeUpdateRequest, RuntimeFunctionDefinitionUpdateRequest, - data_type_service_client::DataTypeServiceClient, - flow_type_service_client::FlowTypeServiceClient, - runtime_function_definition_service_client::RuntimeFunctionDefinitionServiceClient, - }, - shared::{DefinitionDataType as DataType, FlowType, RuntimeFunctionDefinition}, -}; - -pub struct FlowUpdateService { - aquila_url: String, - data_types: Vec, - runtime_definitions: Vec, - flow_types: Vec, +mod error; +mod feature; + +use crate::flow_definition::error::ReaderError; +use crate::flow_definition::feature::Feature; +use crate::flow_definition::feature::version::HasVersion; +use serde::de::DeserializeOwned; +use std::fs; +use std::path::Path; +use tucana::shared::{DefinitionDataType, FlowType, RuntimeFunctionDefinition, Version}; +use walkdir::WalkDir; + +pub struct Reader { + should_break: bool, + accepted_features: Vec, + accepted_version: Option, + path: String, } -impl FlowUpdateService { - /// Create a new FlowUpdateService instance from an Aquila URL and a definition path. - /// - /// This will read the definition files from the given path and initialize the service with the data types, runtime definitions, and flow types. - pub fn from_url(aquila_url: String, definition_path: &str) -> Self { - let mut data_types = Vec::new(); - let mut runtime_definitions = Vec::new(); - let mut flow_types = Vec::new(); - - let definitions = match code0_definition_reader::package::Parser::from_path(definition_path) - { - Some(reader) => reader, - None => { - log::error!("No definition folder found at path: {}", definition_path); - return Self { - aquila_url, - data_types, - runtime_definitions, - flow_types, - }; - } - }; - - for feature in definitions.features { - data_types.append(&mut feature.data_types.clone()); - flow_types.append(&mut feature.flow_types.clone()); - runtime_definitions.append(&mut feature.runtime_functions.clone()); - } - +impl Reader { + pub fn configure( + path: String, + should_break: bool, + accepted_features: Vec, + accepted_version: Option, + ) -> Self { Self { - aquila_url, - data_types, - runtime_definitions, - flow_types, + should_break, + accepted_features, + accepted_version, + path, } } - pub fn with_flow_types(mut self, flow_types: Vec) -> Self { - self.flow_types = flow_types; - self - } + pub fn read_features(&self) -> Result, ReaderError> { + let definitions = Path::new(&self.path); - pub fn with_data_types(mut self, data_types: Vec) -> Self { - self.data_types = data_types; - self - } - - pub fn with_runtime_definitions( - mut self, - runtime_definitions: Vec, - ) -> Self { - self.runtime_definitions = runtime_definitions; - self - } - - pub async fn send(&self) { - self.update_data_types().await; - self.update_runtime_definitions().await; - self.update_flow_types().await; - } - - async fn update_data_types(&self) { - if self.data_types.is_empty() { - log::info!("No data types to update"); - return; - } + match self.read_feature_content(definitions) { + Ok(features) => { + log::info!( + "Loaded {:?} feature/s", + &features + .iter() + .map(|f| f.name.clone()) + .collect::>() + ); - log::info!("Updating the current DataTypes!"); - let mut client = match DataTypeServiceClient::connect(self.aquila_url.clone()).await { - Ok(client) => { - log::info!("Successfully connected to the DataTypeService"); - client - } - Err(err) => { - log::error!("Failed to connect to the DataTypeService: {:?}", err); - return; - } - }; + log::debug!( + "Found FlowTypes {:?}", + &features + .iter() + .flat_map(|f| f.flow_types.iter().map(|t| t.identifier.clone())) + .collect::>() + ); - let request = DataTypeUpdateRequest { - data_types: self.data_types.clone(), - }; + log::debug!( + "Found DataTypes {:?}", + &features + .iter() + .flat_map(|f| f.data_types.iter().map(|t| t.identifier.clone())) + .collect::>() + ); - match client.update(request).await { - Ok(response) => { - log::info!( - "Was the update of the DataTypes accepted by Sagittarius? {}", - response.into_inner().success + log::debug!( + "Found Functions {:?}", + &features + .iter() + .flat_map(|f| f.functions.iter().map(|t| t.runtime_name.clone())) + .collect::>() ); + + Ok(features) } Err(err) => { - log::error!("Failed to update data types: {:?}", err); + log::error!("Failed to read feature/s from {}, {:?}", &self.path, err); + Err(ReaderError::ReadFeatureError { + path: self.path.to_string(), + source: Box::new(err), + }) } } } - async fn update_runtime_definitions(&self) { - if self.runtime_definitions.is_empty() { - log::info!("No runtime definitions to update"); - return; - } + fn read_feature_content(&self, dir: &Path) -> Result, ReaderError> { + let mut features: Vec = Vec::new(); - log::info!("Updating the current RuntimeDefinitions!"); - let mut client = - match RuntimeFunctionDefinitionServiceClient::connect(self.aquila_url.clone()).await { - Ok(client) => { - log::info!("Connected to RuntimeFunctionDefinitionService"); - client - } + let readdir = fs::read_dir(dir).map_err(|err| { + log::error!("Failed to read directory {}: {:?}", dir.display(), err); + ReaderError::ReadDirectoryError { + path: dir.to_path_buf(), + error: err, + } + })?; + + for entry_result in readdir { + let entry = match entry_result { + Ok(entry) => entry, Err(err) => { - log::error!( - "Failed to connect to RuntimeFunctionDefinitionService: {:?}", - err - ); - return; + log::error!("Failed to read directory entry: {:?}", err); + return Err(ReaderError::DirectoryEntryError(err)); } }; - let request = RuntimeFunctionDefinitionUpdateRequest { - runtime_functions: self.runtime_definitions.clone(), - }; + let path = entry.path(); - match client.update(request).await { - Ok(response) => { - log::info!( - "Was the update of the RuntimeFunctionDefinitions accepted by Sagittarius? {}", - response.into_inner().success - ); + if !path.is_dir() { + continue; } - Err(err) => { - log::error!("Failed to update runtime function definitions: {:?}", err); + + let feature_name = path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); + + if !self.accepted_features.is_empty() && !self.accepted_features.contains(&feature_name) + { + log::info!("Skipping not accepted feature: {}", feature_name); + continue; } - } - } - async fn update_flow_types(&self) { - if self.flow_types.is_empty() { - log::info!("No FlowTypes to update!"); - return; + let data_types = match self + .load_definitions_for_feature::(&path, "data_type")? + { + Some(v) => v, + None => continue, + }; + + let flow_types = + match self.load_definitions_for_feature::(&path, "flow_type")? { + Some(v) => v, + None => continue, + }; + + let functions = match self.load_definitions_for_feature::( + &path, + "runtime_definition", + )? { + Some(v) => v, + None => continue, + }; + + let feature = Feature { + name: feature_name, + data_types, + flow_types, + functions, + }; + + features.push(feature); } - log::info!("Updating the current FlowTypes!"); - let mut client = match FlowTypeServiceClient::connect(self.aquila_url.clone()).await { - Ok(client) => { - log::info!("Connected to FlowTypeService!"); - client - } + Ok(features) + } + + fn load_definitions_for_feature( + &self, + feature_dir: &Path, + sub_dir: &str, + ) -> Result>, ReaderError> + where + T: DeserializeOwned + HasVersion, + { + let dir = feature_dir.join(sub_dir); + + let raw: Vec = match self.collect_definitions::(&dir) { + Ok(v) => v, Err(err) => { - log::error!("Failed to connect to FlowTypeService: {:?}", err); - return; + if self.should_break { + return Err(ReaderError::ReadFeatureError { + path: dir.to_string_lossy().to_string(), + source: Box::new(err), + }); + } else { + // Skip this feature if we shouldn't break on error + return Ok(None); + } } }; - let request = FlowTypeUpdateRequest { - flow_types: self.flow_types.clone(), - }; + let items = raw + .into_iter() + .map(|mut v| { + v.normalize_version(); + v + }) + .filter(|v| v.is_accepted(&self.accepted_version)) + .collect(); - match client.update(request).await { - Ok(response) => { - log::info!( - "Was the update of the FlowTypes accepted by Sagittarius? {}", - response.into_inner().success - ); - } - Err(err) => { - log::error!("Failed to update flow types: {:?}", err); + Ok(Some(items)) + } + + fn collect_definitions(&self, dir: &Path) -> Result, ReaderError> + where + T: DeserializeOwned, + { + let mut definitions = Vec::new(); + + if !dir.exists() { + return Ok(definitions); + } + + for entry in WalkDir::new(dir).into_iter().filter_map(Result::ok) { + let path = entry.path(); + + if path.is_file() && path.extension().is_some_and(|ext| ext == "json") { + let content = match fs::read_to_string(path) { + Ok(content) => content, + Err(err) => { + log::error!("Failed to read file {}: {}", path.display(), err); + return Err(ReaderError::ReadFileError { + path: path.to_path_buf(), + error: err, + }); + } + }; + + match serde_json::from_str::(&content) { + Ok(def) => definitions.push(def), + Err(e) => { + if self.should_break { + log::error!("Failed to parse JSON in file {}: {:?}", path.display(), e); + return Err(ReaderError::JsonError { + path: path.to_path_buf(), + error: e, + }); + } else { + log::warn!("Skipping invalid JSON file {}: {:?}", path.display(), e); + } + } + } } } + + Ok(definitions) } } diff --git a/src/flow_service/mod.rs b/src/flow_service/mod.rs new file mode 100644 index 0000000..04ecfa3 --- /dev/null +++ b/src/flow_service/mod.rs @@ -0,0 +1,184 @@ +use crate::flow_definition::Reader; +use tucana::{ + aquila::{ + DataTypeUpdateRequest, FlowTypeUpdateRequest, RuntimeFunctionDefinitionUpdateRequest, + data_type_service_client::DataTypeServiceClient, + flow_type_service_client::FlowTypeServiceClient, + runtime_function_definition_service_client::RuntimeFunctionDefinitionServiceClient, + }, + shared::{DefinitionDataType as DataType, FlowType, RuntimeFunctionDefinition}, +}; + +pub struct FlowUpdateService { + aquila_url: String, + data_types: Vec, + runtime_definitions: Vec, + flow_types: Vec, +} + +impl FlowUpdateService { + /// Create a new FlowUpdateService instance from an Aquila URL and a definition path. + /// + /// This will read the definition files from the given path and initialize the service with the data types, runtime definitions, and flow types. + pub fn from_url(aquila_url: String, definition_path: &str) -> Self { + let mut data_types = Vec::new(); + let mut runtime_definitions = Vec::new(); + let mut flow_types = Vec::new(); + + let reader = Reader::configure(definition_path.to_string(), true, vec![], None); + + let features = match reader.read_features() { + Ok(features) => features, + Err(error) => { + log::error!("Error occurred while reading definitions: {:?}", error); + panic!("Error occurred while reading definitions") + } + }; + + for feature in features { + data_types.append(&mut feature.data_types.clone()); + flow_types.append(&mut feature.flow_types.clone()); + runtime_definitions.append(&mut feature.functions.clone()); + } + + Self { + aquila_url, + data_types, + runtime_definitions, + flow_types, + } + } + + pub fn with_flow_types(mut self, flow_types: Vec) -> Self { + self.flow_types = flow_types; + self + } + + pub fn with_data_types(mut self, data_types: Vec) -> Self { + self.data_types = data_types; + self + } + + pub fn with_runtime_definitions( + mut self, + runtime_definitions: Vec, + ) -> Self { + self.runtime_definitions = runtime_definitions; + self + } + + pub async fn send(&self) { + self.update_data_types().await; + self.update_runtime_definitions().await; + self.update_flow_types().await; + } + + async fn update_data_types(&self) { + if self.data_types.is_empty() { + log::info!("No data types to update"); + return; + } + + log::info!("Updating the current DataTypes!"); + let mut client = match DataTypeServiceClient::connect(self.aquila_url.clone()).await { + Ok(client) => { + log::info!("Successfully connected to the DataTypeService"); + client + } + Err(err) => { + log::error!("Failed to connect to the DataTypeService: {:?}", err); + return; + } + }; + + let request = DataTypeUpdateRequest { + data_types: self.data_types.clone(), + }; + + match client.update(request).await { + Ok(response) => { + log::info!( + "Was the update of the DataTypes accepted by Sagittarius? {}", + response.into_inner().success + ); + } + Err(err) => { + log::error!("Failed to update data types: {:?}", err); + } + } + } + + async fn update_runtime_definitions(&self) { + if self.runtime_definitions.is_empty() { + log::info!("No runtime definitions to update"); + return; + } + + log::info!("Updating the current RuntimeDefinitions!"); + let mut client = + match RuntimeFunctionDefinitionServiceClient::connect(self.aquila_url.clone()).await { + Ok(client) => { + log::info!("Connected to RuntimeFunctionDefinitionService"); + client + } + Err(err) => { + log::error!( + "Failed to connect to RuntimeFunctionDefinitionService: {:?}", + err + ); + return; + } + }; + + let request = RuntimeFunctionDefinitionUpdateRequest { + runtime_functions: self.runtime_definitions.clone(), + }; + + match client.update(request).await { + Ok(response) => { + log::info!( + "Was the update of the RuntimeFunctionDefinitions accepted by Sagittarius? {}", + response.into_inner().success + ); + } + Err(err) => { + log::error!("Failed to update runtime function definitions: {:?}", err); + } + } + } + + async fn update_flow_types(&self) { + if self.flow_types.is_empty() { + log::info!("No FlowTypes to update!"); + return; + } + + log::info!("Updating the current FlowTypes!"); + let mut client = match FlowTypeServiceClient::connect(self.aquila_url.clone()).await { + Ok(client) => { + log::info!("Connected to FlowTypeService!"); + client + } + Err(err) => { + log::error!("Failed to connect to FlowTypeService: {:?}", err); + return; + } + }; + + let request = FlowTypeUpdateRequest { + flow_types: self.flow_types.clone(), + }; + + match client.update(request).await { + Ok(response) => { + log::info!( + "Was the update of the FlowTypes accepted by Sagittarius? {}", + response.into_inner().success + ); + } + Err(err) => { + log::error!("Failed to update flow types: {:?}", err); + } + } + } +} diff --git a/src/flow_validator/mod.rs b/src/flow_validator/mod.rs index e1db221..a2dac68 100644 --- a/src/flow_validator/mod.rs +++ b/src/flow_validator/mod.rs @@ -71,7 +71,7 @@ fn verify_data_type_rules( } } Config::ContainsType(config) => { - match apply_contains_type(config, &availabe_data_types, &body) { + match apply_contains_type(config, availabe_data_types, &body) { Ok(_) => continue, Err(violation) => { violations.extend(violation.violations); @@ -89,7 +89,7 @@ fn verify_data_type_rules( }; } Config::ContainsKey(config) => { - match apply_contains_key(config, &body, &availabe_data_types) { + match apply_contains_key(config, &body, availabe_data_types) { Ok(_) => continue, Err(violation) => { violations.extend(violation.violations); diff --git a/src/flow_validator/rule/contains_key.rs b/src/flow_validator/rule/contains_key.rs index f95538b..1cb9b24 100644 --- a/src/flow_validator/rule/contains_key.rs +++ b/src/flow_validator/rule/contains_key.rs @@ -2,12 +2,12 @@ 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; -use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules}; /// # Data Type Validation Behavior /// @@ -31,7 +31,7 @@ pub fn apply_contains_key( let identifier = rule.data_type_identifier; if let Some(Kind::StructValue(_)) = &body.kind { - let value = match expect_kind(&identifier, &body) { + let value = match expect_kind(&identifier, body) { Some(value) => Value { kind: Some(value.to_owned()), }, @@ -46,7 +46,7 @@ pub fn apply_contains_key( } }; - let data_type = match get_data_type_by_id(&available_data_types, &identifier) { + let data_type = match get_data_type_by_id(available_data_types, &identifier) { Some(data_type) => data_type, None => { let error = MissingDataTypeRuleDefinition { @@ -59,14 +59,14 @@ pub fn apply_contains_key( } }; - return verify_data_type_rules(value, data_type, available_data_types); + verify_data_type_rules(value, data_type, available_data_types) } else { - return Err(DataTypeRuleError { + 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 index 8b98245..97a00ff 100644 --- a/src/flow_validator/rule/contains_type.rs +++ b/src/flow_validator/rule/contains_type.rs @@ -1,8 +1,8 @@ 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, }; -use crate::flow_validator::{get_data_type_by_id, verify_data_type_rules}; /// # Item of Collection Validation /// @@ -42,7 +42,7 @@ pub fn apply_contains_type( let mut rule_errors: Option = None; for value in list.values { - match verify_data_type_rules(value, data_type.clone(), &available_data_types) { + match verify_data_type_rules(value, data_type.clone(), available_data_types) { Ok(_) => {} Err(errors) => { rule_errors = Some(errors); diff --git a/src/flow_validator/rule/mod.rs b/src/flow_validator/rule/mod.rs index e793609..f39a8a0 100644 --- a/src/flow_validator/rule/mod.rs +++ b/src/flow_validator/rule/mod.rs @@ -3,4 +3,4 @@ pub mod contains_type; pub mod item_of_collection; pub mod number_range; pub mod regex; -pub mod violation; \ No newline at end of file +pub mod violation; diff --git a/src/flow_validator/rule/number_range.rs b/src/flow_validator/rule/number_range.rs index 8af4fca..e9b28b2 100644 --- a/src/flow_validator/rule/number_range.rs +++ b/src/flow_validator/rule/number_range.rs @@ -30,7 +30,7 @@ pub fn apply_number_range( }; let result = match kind { - Kind::NumberValue(n) => n.clone(), + Kind::NumberValue(n) => *n, _ => { return Err(DataTypeRuleError { violations: vec![DataTypeRuleViolation::RegexTypeNotAccepted( diff --git a/src/flow_validator/rule/regex.rs b/src/flow_validator/rule/regex.rs index 26ddd51..fb15672 100644 --- a/src/flow_validator/rule/regex.rs +++ b/src/flow_validator/rule/regex.rs @@ -1,7 +1,7 @@ -use tucana::shared::{DataTypeRegexRuleConfig, Value, value::Kind}; use super::violation::{ DataTypeRuleError, DataTypeRuleViolation, RegexRuleTypeNotAcceptedViolation, RegexRuleViolation, }; +use tucana::shared::{DataTypeRegexRuleConfig, Value, value::Kind}; /// # Regex Pattern Validation /// @@ -51,11 +51,11 @@ pub fn apply_regex(rule: DataTypeRegexRuleConfig, body: &Value) -> Result<(), Da let regex = regex::Regex::new(rule.pattern.as_str()).unwrap(); if !regex.is_match(&result) { - return Err(DataTypeRuleError { + Err(DataTypeRuleError { violations: vec![DataTypeRuleViolation::Regex(RegexRuleViolation { missing_regex: rule.pattern.clone(), })], - }); + }) } else { Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 4b1b216..d857809 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,6 @@ pub mod flow_health; #[cfg(feature = "flow_validator")] pub mod flow_validator; + +#[cfg(feature = "flow_service")] +pub mod flow_service;