This is an automated email from the ASF dual-hosted git repository. martinzink pushed a commit to branch minifi_rust_impr in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
commit 37bc79d4559086b0e91fb2ea187338ddbcd3ce81 Author: Martin Zink <[email protected]> AuthorDate: Wed Jul 8 10:30:49 2026 +0200 minifi_rust improvements --- .../extensions/minifi_rs_playground/Cargo.toml | 1 + .../controller_services/dog_controller_service.rs | 18 ++- .../lorem_ipsum_controller_service.rs | 11 +- .../src/processors/generate_flow_file.rs | 28 ++--- .../processors/generate_flow_file/properties.rs | 21 +--- .../src/processors/get_file.rs | 39 +++--- .../src/processors/get_file/properties.rs | 45 ++----- .../src/processors/kamikaze_processor.rs | 24 ++-- .../processors/kamikaze_processor/properties.rs | 15 +-- .../src/processors/log_attribute.rs | 27 ++--- .../src/processors/log_attribute/properties.rs | 32 ++--- .../src/processors/lorem_ipsum_cs_user.rs | 11 +- .../processors/lorem_ipsum_cs_user/properties.rs | 11 +- .../src/processors/put_file.rs | 24 ++-- .../src/processors/put_file/properties.rs | 20 +-- .../processors/put_file/unix_only_properties.rs | 11 +- .../src/processors/zoo_processor.rs | 11 +- minifi_rust/minifi_native/Cargo.toml | 3 + minifi_rust/minifi_native/src/api.rs | 2 +- minifi_rust/minifi_native/src/api/attribute.rs | 4 + minifi_rust/minifi_native/src/api/errors.rs | 27 ++++- minifi_rust/minifi_native/src/api/logger.rs | 18 ++- .../minifi_native/src/api/process_context.rs | 79 +----------- .../api/processor_wrappers/flow_file_transform.rs | 52 ++++++++ .../utils/context_session_flowfile_bundle.rs | 2 +- minifi_rust/minifi_native/src/api/property.rs | 135 ++++++++++++++------- minifi_rust/minifi_native/src/api/relationship.rs | 8 ++ .../src/c_ffi/c_ffi_controller_service_context.rs | 2 +- .../minifi_native/src/c_ffi/c_ffi_property.rs | 24 ++-- minifi_rust/minifi_native/src/lib.rs | 6 +- .../src/mock/mock_controller_service_context.rs | 2 +- minifi_rust/minifi_native_macros/src/lib.rs | 21 ++++ minifi_rust/minifi_rs_behave/Dockerfile.alpine | 1 + minifi_rust/minifi_rs_behave/Dockerfile.debian | 8 +- minifi_rust/minifi_rs_behave/src/main.rs | 1 + 35 files changed, 373 insertions(+), 371 deletions(-) diff --git a/minifi_rust/extensions/minifi_rs_playground/Cargo.toml b/minifi_rust/extensions/minifi_rs_playground/Cargo.toml index 8d8682660..ec90190e3 100644 --- a/minifi_rust/extensions/minifi_rs_playground/Cargo.toml +++ b/minifi_rust/extensions/minifi_rs_playground/Cargo.toml @@ -16,6 +16,7 @@ hex = "0.4.3" strum_macros = "0.28.0" lipsum = "0.9.1" + [dev-dependencies] tempfile = "3.22.0" filetime = "0.2.26" diff --git a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs index df2521213..2fa133742 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/dog_controller_service.rs @@ -19,6 +19,7 @@ use crate::controller_services::animal_controller_apis::{ CanFlyControllerApi, NumberOfLegsControllerApi, }; use minifi_native::ControllerServiceApi; +use minifi_native::PropertyConstraints::{NoConstraints, Validator}; use minifi_native::macros::ComponentIdentifier; use minifi_native::{ ControllerServiceDefinition, EnableControllerService, GetProperty, Logger, MinifiError, @@ -32,9 +33,7 @@ pub(crate) const HAS_JETPACK: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const EXTRA_INFO: Property = Property { @@ -44,9 +43,7 @@ pub(crate) const EXTRA_INFO: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; #[allow(dead_code)] // extra_info is only used by {:?} @@ -73,11 +70,10 @@ impl EnableControllerService for DogControllerRs { where Self: Sized, { - let has_jetpack = context.get_bool_property(&HAS_JETPACK)?.ok_or( - MinifiError::missing_required_property("Has jetpack is required"), - )?; - - let extra_info = context.get_property(&EXTRA_INFO)?.unwrap_or("".into()); + let has_jetpack = context.get_req_property::<bool>(&HAS_JETPACK)?; + let extra_info = context + .get_property::<String>(&EXTRA_INFO)? + .unwrap_or("".into()); Ok(Self { has_jetpack, diff --git a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs index 25341971e..7c692ba39 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/controller_services/lorem_ipsum_controller_service.rs @@ -16,6 +16,7 @@ // under the License. use lipsum::lipsum; +use minifi_native::PropertyConstraints::Validator; use minifi_native::macros::ComponentIdentifier; use minifi_native::{ ControllerServiceDefinition, EnableControllerService, GetProperty, Logger, MinifiError, @@ -29,9 +30,7 @@ const LENGTH: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("25"), - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::U64Validator), }; #[derive(Debug, ComponentIdentifier)] @@ -44,11 +43,7 @@ impl EnableControllerService for LoremIpsumControllerService { where Self: Sized, { - let length = context - .get_u64_property(&LENGTH)? - .ok_or(MinifiError::missing_required_property("Length is required"))?; - - let data = lipsum(length as usize); + let data = lipsum(context.get_req_property::<usize>(&LENGTH)?); Ok(Self { data }) } } diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs index 55bb376da..4464ef4e7 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file.rs @@ -19,8 +19,8 @@ use minifi_native::macros::ComponentIdentifier; use minifi_native::{ - GetProperty, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, Schedule, - Trigger, + DataSize, GetProperty, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, + Schedule, Trigger, }; use rand::RngExt; use rand::distr::Alphanumeric; @@ -52,22 +52,14 @@ impl Schedule for GenerateFlowFileRs { where Self: Sized, { - let is_unique = context - .get_bool_property(&properties::UNIQUE_FLOW_FILES)? - .expect("Required property"); - let is_text = context - .get_property(&properties::DATA_FORMAT)? - .expect("Required property") - .as_str() - == "Text"; - let has_custom_text = context.get_property(&properties::CUSTOM_TEXT)?.is_some(); - - let file_size = context - .get_size_property(&properties::FILE_SIZE)? - .expect("Required property"); - let batch_size = context - .get_u64_property(&properties::BATCH_SIZE)? - .expect("Required property"); + let is_unique = context.get_req_property::<bool>(&properties::UNIQUE_FLOW_FILES)?; + let is_text = context.get_req_property::<String>(&properties::DATA_FORMAT)? == "Text"; + let has_custom_text = context + .get_property::<String>(&properties::CUSTOM_TEXT)? + .is_some(); + + let file_size = context.get_req_property::<DataSize>(&properties::FILE_SIZE)?; + let batch_size = context.get_req_property::<u64>(&properties::BATCH_SIZE)?; let mode = Self::get_mode(is_unique, is_text, has_custom_text, file_size); let data_generated_during_on_schedule = diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs index 16d0b8a71..468b76f7c 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/generate_flow_file/properties.rs @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +use minifi_native::PropertyConstraints::{AllowedValues, NoConstraints, Validator}; use minifi_native::{Property, StandardPropertyValidator}; pub(crate) const FILE_SIZE: Property = Property { @@ -24,9 +25,7 @@ pub(crate) const FILE_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: Some("1 kB"), - validator: StandardPropertyValidator::DataSizeValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::DataSizeValidator), }; pub(crate) const BATCH_SIZE: Property = Property { @@ -36,9 +35,7 @@ pub(crate) const BATCH_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("1"), - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::U64Validator), }; pub(crate) const DATA_FORMAT: Property = Property { @@ -48,9 +45,7 @@ pub(crate) const DATA_FORMAT: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("Binary"), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &["Text", "Binary"], - allowed_type: None, + constraints: AllowedValues(&["Text", "Binary"]), }; pub(crate) const UNIQUE_FLOW_FILES: Property = Property { @@ -60,9 +55,7 @@ pub(crate) const UNIQUE_FLOW_FILES: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const CUSTOM_TEXT: Property = Property { @@ -72,7 +65,5 @@ pub(crate) const CUSTOM_TEXT: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs index 1847ea632..dc365e9c4 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file.rs @@ -26,8 +26,8 @@ use crate::processors::get_file::properties::{ }; use minifi_native::macros::ComponentIdentifier; use minifi_native::{ - GetProperty, IoState, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, - Schedule, Trigger, debug, info, trace, warn, + DataSize, GetProperty, IoState, Logger, MinifiError, OnTriggerResult, ProcessContext, + ProcessSession, Schedule, Trigger, debug, info, trace, warn, }; use std::collections::VecDeque; use std::error; @@ -223,10 +223,7 @@ impl Schedule for GetFileRs { where Self: Sized, { - let input_directory: PathBuf = context - .get_property(&DIRECTORY)? - .expect("Required property") - .into(); + let input_directory = context.get_req_property::<PathBuf>(&DIRECTORY)?; if !input_directory.is_dir() { return Err(MinifiError::schedule_err(format!( "{:?} is not a valid directory", @@ -234,25 +231,17 @@ impl Schedule for GetFileRs { ))); } - let recursive = context - .get_bool_property(&RECURSE)? - .expect("Required property"); - - let keep_source_file = context - .get_bool_property(&KEEP_SOURCE_FILE)? - .expect("Required property"); - - let poll_interval = context.get_duration_property(&properties::POLLING_INTERVAL)?; - let min_size = context.get_size_property(&MIN_SIZE)?; - let max_size = context.get_size_property(&MAX_SIZE)?; - let min_age = context.get_duration_property(&MIN_AGE)?; - let max_age = context.get_duration_property(&MAX_AGE)?; - let batch_size = context - .get_u64_property(&BATCH_SIZE)? - .expect("required property"); - let ignore_hidden_files = context - .get_bool_property(&IGNORE_HIDDEN_FILES)? - .expect("required property"); + let recursive = context.get_req_property::<bool>(&RECURSE)?; + + let keep_source_file = context.get_req_property::<bool>(&KEEP_SOURCE_FILE)?; + + let poll_interval = context.get_property::<Duration>(&properties::POLLING_INTERVAL)?; + let min_size = context.get_property::<DataSize>(&MIN_SIZE)?; + let max_size = context.get_property::<DataSize>(&MAX_SIZE)?; + let min_age = context.get_property::<Duration>(&MIN_AGE)?; + let max_age = context.get_property::<Duration>(&MAX_AGE)?; + let batch_size = context.get_req_property::<u64>(&BATCH_SIZE)?; + let ignore_hidden_files = context.get_req_property::<bool>(&IGNORE_HIDDEN_FILES)?; Ok(GetFileRs { recursive, diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs index b43009b28..f27d83841 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/get_file/properties.rs @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +use minifi_native::PropertyConstraints::Validator; use minifi_native::{Property, StandardPropertyValidator}; pub(crate) const DIRECTORY: Property = Property { @@ -24,33 +25,27 @@ pub(crate) const DIRECTORY: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: None, - validator: StandardPropertyValidator::NonBlankValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::NonBlankValidator), }; pub(crate) const RECURSE: Property = Property { name: "Recurse Subdirectories", description: "Indicates whether or not to pull files from subdirectories", - is_required: false, + is_required: true, is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const KEEP_SOURCE_FILE: Property = Property { name: "Keep Source File", description: "If true, the file is not deleted after it has been copied to the Content Repository", - is_required: false, + is_required: true, is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const MIN_AGE: Property = Property { @@ -60,9 +55,7 @@ pub(crate) const MIN_AGE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::TimePeriodValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::TimePeriodValidator), }; pub(crate) const MAX_AGE: Property = Property { @@ -72,9 +65,7 @@ pub(crate) const MAX_AGE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::TimePeriodValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::TimePeriodValidator), }; pub(crate) const MIN_SIZE: Property = Property { @@ -84,9 +75,7 @@ pub(crate) const MIN_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::DataSizeValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::DataSizeValidator), }; pub(crate) const MAX_SIZE: Property = Property { @@ -96,9 +85,7 @@ pub(crate) const MAX_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::DataSizeValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::DataSizeValidator), }; pub(crate) const IGNORE_HIDDEN_FILES: Property = Property { @@ -108,9 +95,7 @@ pub(crate) const IGNORE_HIDDEN_FILES: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const POLLING_INTERVAL: Property = Property { @@ -120,9 +105,7 @@ pub(crate) const POLLING_INTERVAL: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::TimePeriodValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::TimePeriodValidator), }; pub(crate) const BATCH_SIZE: Property = Property { @@ -132,7 +115,5 @@ pub(crate) const BATCH_SIZE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("10"), - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::U64Validator), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs index 6686c0b40..f215d8c78 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor.rs @@ -21,15 +21,19 @@ mod properties; mod relationships; use crate::controller_services::lorem_ipsum_controller_service::LoremIpsumControllerService; -use crate::processors::kamikaze_processor::properties::NOT_REGISTERED_PROPERTY; -use minifi_native::macros::ComponentIdentifier; +use crate::processors::kamikaze_processor::properties::{ + NOT_REGISTERED_PROPERTY, SCHEDULE_BEHAVIOUR, TRIGGER_BEHAVIOUR, +}; +use minifi_native::macros::{ComponentIdentifier, PropertyType}; use minifi_native::{ GetProperty, Logger, MinifiError, OnTriggerResult, ProcessContext, ProcessSession, Schedule, Trigger, }; use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; -#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr)] +#[derive( + Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr, PropertyType, +)] #[strum(serialize_all = "PascalCase", const_into_str)] enum KamikazeBehaviour { ReturnErr, @@ -49,15 +53,11 @@ impl Schedule for KamikazeProcessorRs { where Self: Sized, { - let trigger_behaviour = context - .get_property(&properties::TRIGGER_BEHAVIOUR)? - .expect("required property") - .parse::<KamikazeBehaviour>()?; + let trigger_behaviour = + context.get_req_property::<KamikazeBehaviour>(&TRIGGER_BEHAVIOUR)?; - let schedule_behaviour = context - .get_property(&properties::SCHEDULE_BEHAVIOUR)? - .expect("required property") - .parse::<KamikazeBehaviour>()?; + let schedule_behaviour = + context.get_req_property::<KamikazeBehaviour>(&SCHEDULE_BEHAVIOUR)?; match schedule_behaviour { KamikazeBehaviour::ReturnErr => Err(MinifiError::schedule_err( @@ -65,7 +65,7 @@ impl Schedule for KamikazeProcessorRs { )), KamikazeBehaviour::ReturnOk => Ok(KamikazeProcessorRs { trigger_behaviour }), KamikazeBehaviour::GetNotRegisteredProperty => { - let _ = context.get_property(&NOT_REGISTERED_PROPERTY)?; + let _ = context.get_property::<String>(&NOT_REGISTERED_PROPERTY)?; Ok(KamikazeProcessorRs { trigger_behaviour }) } KamikazeBehaviour::Panic => { diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs index 6ee86b1bd..1db6c3ada 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/kamikaze_processor/properties.rs @@ -16,7 +16,8 @@ // under the License. use crate::processors::kamikaze_processor::KamikazeBehaviour; -use minifi_native::{Property, StandardPropertyValidator}; +use minifi_native::Property; +use minifi_native::PropertyConstraints::{AllowedValues, NoConstraints}; use strum::VariantNames; pub(crate) const SCHEDULE_BEHAVIOUR: Property = Property { @@ -26,9 +27,7 @@ pub(crate) const SCHEDULE_BEHAVIOUR: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(KamikazeBehaviour::ReturnOk.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: KamikazeBehaviour::VARIANTS, - allowed_type: None, + constraints: AllowedValues(KamikazeBehaviour::VARIANTS), }; pub(crate) const TRIGGER_BEHAVIOUR: Property = Property { @@ -38,9 +37,7 @@ pub(crate) const TRIGGER_BEHAVIOUR: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(KamikazeBehaviour::ReturnOk.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: KamikazeBehaviour::VARIANTS, - allowed_type: None, + constraints: AllowedValues(KamikazeBehaviour::VARIANTS), }; pub(crate) const NOT_REGISTERED_PROPERTY: Property = Property { @@ -50,7 +47,5 @@ pub(crate) const NOT_REGISTERED_PROPERTY: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs index 3e59e2d52..c59eb5096 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute.rs @@ -121,25 +121,15 @@ impl Trigger for LogAttributeRs { impl Schedule for LogAttributeRs { fn schedule<P: GetProperty, L>(context: &P, _logger: &L) -> Result<Self, MinifiError> { - let log_level = context - .get_property(&LOG_LEVEL)? - .expect("required property") - .parse::<LogLevel>()?; - - let log_payload = context - .get_bool_property(&LOG_PAYLOAD)? - .expect("required property"); - - let flow_files_to_log = context - .get_property(&FLOW_FILES_TO_LOG)? - .expect("required property") - .parse::<usize>()?; + let log_level = context.get_req_property::<LogLevel>(&LOG_LEVEL)?; + let log_payload = context.get_req_property::<bool>(&LOG_PAYLOAD)?; + let flow_files_to_log = context.get_req_property::<usize>(&FLOW_FILES_TO_LOG)?; fn get_csv_property<P: GetProperty>( context: &P, property: &Property, ) -> Result<Option<Vec<String>>, MinifiError> { - Ok(context.get_property(property)?.map(|s| { + Ok(context.get_property::<String>(property)?.map(|s| { s.split(',') .map(|s| s.trim().to_string()) .collect::<Vec<String>>() @@ -152,13 +142,12 @@ impl Schedule for LogAttributeRs { let dash_line = format!( "{:-^50}", context - .get_property(&properties::LOG_PREFIX)? - .unwrap_or(String::new()) + .get_property::<String>(&properties::LOG_PREFIX)? + .unwrap_or_default() ); - let hex_encode_payload = context - .get_bool_property(&properties::HEX_ENCODE_PAYLOAD)? - .expect("required property"); + let hex_encode_payload = + context.get_req_property::<bool>(&properties::HEX_ENCODE_PAYLOAD)?; Ok(LogAttributeRs { log_level, diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs index 48002c92f..a8bf651ae 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/log_attribute/properties.rs @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +use minifi_native::PropertyConstraints::{AllowedValues, NoConstraints, Validator}; +use minifi_native::StandardPropertyValidator::U64Validator; use minifi_native::{LogLevel, Property, StandardPropertyValidator}; use strum::VariantNames; @@ -24,10 +26,8 @@ pub(crate) const LOG_LEVEL: Property = Property { is_required: true, is_sensitive: false, supports_expr_lang: false, - default_value: Some("Info"), // todo! it would be nicer to come from enum value but wasnt able to use into_const_str from another crate - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: LogLevel::VARIANTS, - allowed_type: None, + default_value: Some(LogLevel::Info.into_str()), + constraints: AllowedValues(LogLevel::VARIANTS), }; pub(crate) const ATTRIBUTES_TO_LOG: Property = Property { @@ -37,9 +37,7 @@ pub(crate) const ATTRIBUTES_TO_LOG: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; pub(crate) const ATTRIBUTES_TO_IGNORE: Property = Property { @@ -49,9 +47,7 @@ pub(crate) const ATTRIBUTES_TO_IGNORE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; pub(crate) const LOG_PAYLOAD: Property = Property { @@ -61,9 +57,7 @@ pub(crate) const LOG_PAYLOAD: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const LOG_PREFIX: Property = Property { @@ -73,9 +67,7 @@ pub(crate) const LOG_PREFIX: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; pub(crate) const FLOW_FILES_TO_LOG: Property = Property { @@ -85,9 +77,7 @@ pub(crate) const FLOW_FILES_TO_LOG: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("1"), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(U64Validator), }; pub(crate) const HEX_ENCODE_PAYLOAD: Property = Property { @@ -97,7 +87,5 @@ pub(crate) const HEX_ENCODE_PAYLOAD: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("false"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs index 2c4cdb517..b44fcc811 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user.rs @@ -21,7 +21,7 @@ mod properties; use crate::controller_services::lorem_ipsum_controller_service::LoremIpsumControllerService; use crate::processors::lorem_ipsum_cs_user::properties::CONTROLLER_SERVICE; use crate::processors::lorem_ipsum_cs_user::relationships::SUCCESS; -use minifi_native::macros::ComponentIdentifier; +use minifi_native::macros::{ComponentIdentifier, PropertyType}; use minifi_native::{ Content, FlowFileSource, GeneratedFlowFile, GetControllerService, GetProperty, Logger, MinifiError, Schedule, trace, @@ -29,7 +29,9 @@ use minifi_native::{ use std::collections::HashMap; use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; -#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr)] +#[derive( + Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr, PropertyType, +)] #[strum(serialize_all = "PascalCase", const_into_str)] enum WriteMethod { Buffer, @@ -46,10 +48,7 @@ impl Schedule for LoremIpsumCSUser { where Self: Sized, { - let write_method = context - .get_property(&properties::WRITE_METHOD)? - .expect("required property") - .parse::<WriteMethod>()?; + let write_method = context.get_req_property::<WriteMethod>(&properties::WRITE_METHOD)?; Ok(Self { write_method }) } } diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs index b78ab6d23..57a00ebfa 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/lorem_ipsum_cs_user/properties.rs @@ -17,7 +17,8 @@ use crate::controller_services::lorem_ipsum_controller_service::LoremIpsumControllerService; use minifi_native::ComponentIdentifier; -use minifi_native::{Property, StandardPropertyValidator}; +use minifi_native::Property; +use minifi_native::PropertyConstraints::{AllowedType, AllowedValues}; use strum::VariantNames; pub(crate) const CONTROLLER_SERVICE: Property = Property { @@ -27,9 +28,7 @@ pub(crate) const CONTROLLER_SERVICE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: Some(LoremIpsumControllerService::CLASS_NAME), + constraints: AllowedType(LoremIpsumControllerService::CLASS_NAME), }; pub(crate) const WRITE_METHOD: Property = Property { @@ -39,7 +38,5 @@ pub(crate) const WRITE_METHOD: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(super::WriteMethod::Buffer.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: super::WriteMethod::VARIANTS, - allowed_type: None, + constraints: AllowedValues(super::WriteMethod::VARIANTS), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs index 4d554c132..85bc5fb4a 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file.rs @@ -18,7 +18,7 @@ // This is the (not production ready) reimplementation of the already existing standard PutFile processor use crate::processors::put_file::relationships::{FAILURE, SUCCESS}; -use minifi_native::macros::ComponentIdentifier; +use minifi_native::macros::{ComponentIdentifier, PropertyType}; use minifi_native::{ FlowFileTransform, GetAttribute, GetControllerService, GetId, GetProperty, InputStream, Logger, MinifiError, Schedule, TransformedFlowFile, trace, warn, @@ -32,7 +32,9 @@ mod relationships; #[cfg(unix)] mod unix_only_properties; -#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr)] +#[derive( + Debug, Clone, Copy, PartialEq, Display, EnumString, VariantNames, IntoStaticStr, PropertyType, +)] #[strum(serialize_all = "camelCase", const_into_str)] enum ConflictResolutionStrategy { Fail, @@ -109,14 +111,12 @@ impl PutFileRs { where Ctx: GetProperty + GetAttribute + GetId, { - let directory = context - .get_property(&properties::DIRECTORY)? - .expect("required property"); + let directory = context.get_req_property::<PathBuf>(&properties::DIRECTORY)?; let file_name = context .get_attribute("filename")? .unwrap_or(context.get_id()?); - Ok(PathBuf::from(directory).join(file_name)) + Ok(directory.join(file_name)) } fn prepare_destination(&self, destination: &Path) -> std::io::Result<()> { @@ -163,7 +163,7 @@ impl PutFileRs { let parse_permission = |property: &minifi_native::Property| -> Result<Option<std::fs::Permissions>, MinifiError> { Ok(context - .get_property(property)? + .get_property::<String>(property)? .map(|perm_str| u32::from_str_radix(&perm_str, 8)) .transpose()? .map(std::fs::Permissions::from_mode)) @@ -188,15 +188,11 @@ impl PutFileRs { impl Schedule for PutFileRs { fn schedule<P: GetProperty, L: Logger>(context: &P, _logger: &L) -> Result<Self, MinifiError> { let conflict_resolution_strategy = context - .get_property(&properties::CONFLICT_RESOLUTION)? - .expect("required property") - .parse::<ConflictResolutionStrategy>()?; + .get_req_property::<ConflictResolutionStrategy>(&properties::CONFLICT_RESOLUTION)?; - let try_make_dirs = context - .get_bool_property(&properties::CREATE_DIRS)? - .expect("required property"); + let try_make_dirs = context.get_req_property::<bool>(&properties::CREATE_DIRS)?; - let maximum_file_count = context.get_u64_property(&properties::MAX_FILE_COUNT)?; + let maximum_file_count = context.get_property::<u64>(&properties::MAX_FILE_COUNT)?; let unix_permissions = Self::parse_unix_permissions(context)?; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs index 4facd860f..f25e8fc47 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/properties.rs @@ -15,10 +15,10 @@ // specific language governing permissions and limitations // under the License. +use super::ConflictResolutionStrategy; +use minifi_native::PropertyConstraints::{AllowedValues, Validator}; use minifi_native::{Property, StandardPropertyValidator}; use strum::VariantNames; - -use super::ConflictResolutionStrategy; pub(crate) const DIRECTORY: Property = Property { name: "Directory", description: "The output directory to which to put files", @@ -26,9 +26,7 @@ pub(crate) const DIRECTORY: Property = Property { is_sensitive: false, supports_expr_lang: true, default_value: Some("."), - validator: StandardPropertyValidator::NonBlankValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::NonBlankValidator), }; pub(crate) const CONFLICT_RESOLUTION: Property = Property { @@ -38,9 +36,7 @@ pub(crate) const CONFLICT_RESOLUTION: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some(ConflictResolutionStrategy::Fail.into_str()), - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: ConflictResolutionStrategy::VARIANTS, - allowed_type: None, + constraints: AllowedValues(ConflictResolutionStrategy::VARIANTS), }; pub(crate) const CREATE_DIRS: Property = Property { @@ -50,9 +46,7 @@ pub(crate) const CREATE_DIRS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: Some("true"), - validator: StandardPropertyValidator::BoolValidator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::BoolValidator), }; pub(crate) const MAX_FILE_COUNT: Property = Property { @@ -62,7 +56,5 @@ pub(crate) const MAX_FILE_COUNT: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, // Diverged from the original implementation, u64 with no default describes the behavior better - validator: StandardPropertyValidator::U64Validator, - allowed_values: &[], - allowed_type: None, + constraints: Validator(StandardPropertyValidator::U64Validator), }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs index f7cbd7811..e5e99f229 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/put_file/unix_only_properties.rs @@ -15,7 +15,8 @@ // specific language governing permissions and limitations // under the License. -use minifi_native::{Property, StandardPropertyValidator}; +use minifi_native::Property; +use minifi_native::PropertyConstraints::NoConstraints; pub(crate) const PERMISSIONS: Property = Property { name: "Permissions", @@ -24,9 +25,7 @@ pub(crate) const PERMISSIONS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; pub(crate) const DIRECTORY_PERMISSIONS: Property = Property { @@ -36,7 +35,5 @@ pub(crate) const DIRECTORY_PERMISSIONS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: None, + constraints: NoConstraints, }; diff --git a/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs b/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs index 2bbdb9a55..dc166722d 100644 --- a/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs +++ b/minifi_rust/extensions/minifi_rs_playground/src/processors/zoo_processor.rs @@ -19,11 +19,12 @@ use crate::controller_services::animal_controller_apis::{ CanFlyControllerApi, NumberOfLegsControllerApi, }; use minifi_native::ControllerServiceApi; +use minifi_native::PropertyConstraints::AllowedType; use minifi_native::macros::ComponentIdentifier; use minifi_native::{ GetProperty, Logger, MinifiError, OnTriggerResult, OutputAttribute, ProcessContext, ProcessSession, ProcessorDefinition, ProcessorInputRequirement, Property, Relationship, - Schedule, StandardPropertyValidator, Trigger, critical, info, + Schedule, Trigger, critical, info, }; pub(crate) const CAN_FLY_SERVICE: Property = Property { @@ -33,9 +34,7 @@ pub(crate) const CAN_FLY_SERVICE: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: Some(<dyn CanFlyControllerApi>::INTERFACE_NAME), + constraints: AllowedType(<dyn CanFlyControllerApi>::INTERFACE_NAME), }; pub(crate) const NUMBER_OF_LEGS: Property = Property { @@ -45,9 +44,7 @@ pub(crate) const NUMBER_OF_LEGS: Property = Property { is_sensitive: false, supports_expr_lang: false, default_value: None, - validator: StandardPropertyValidator::AlwaysValidValidator, - allowed_values: &[], - allowed_type: Some(<dyn NumberOfLegsControllerApi>::INTERFACE_NAME), + constraints: AllowedType(<dyn NumberOfLegsControllerApi>::INTERFACE_NAME), }; #[derive(Debug, ComponentIdentifier)] diff --git a/minifi_rust/minifi_native/Cargo.toml b/minifi_rust/minifi_native/Cargo.toml index 5998c4fd3..b3f75648b 100644 --- a/minifi_rust/minifi_native/Cargo.toml +++ b/minifi_rust/minifi_native/Cargo.toml @@ -13,3 +13,6 @@ strum_macros = "0.28.0" humantime = "2.3.0" byte-unit = "5.1.6" itertools = "0.14.0" + +[features] +test-utils = [] diff --git a/minifi_rust/minifi_native/src/api.rs b/minifi_rust/minifi_native/src/api.rs index 6152b94ca..066fc91c3 100644 --- a/minifi_rust/minifi_native/src/api.rs +++ b/minifi_rust/minifi_native/src/api.rs @@ -38,6 +38,6 @@ pub use process_session::{InputStream, OutputStream, ProcessSession}; pub use raw_controller_service::RawControllerService; pub use raw_processor::{OnTriggerResult, ProcessorInputRequirement, RawProcessor, ThreadingModel}; -pub use property::StandardPropertyValidator; +pub use property::{DataSize, PropertyConstraints, PropertyType, StandardPropertyValidator}; pub use relationship::Relationship; diff --git a/minifi_rust/minifi_native/src/api/attribute.rs b/minifi_rust/minifi_native/src/api/attribute.rs index 9e6b316eb..571ea7ce7 100644 --- a/minifi_rust/minifi_native/src/api/attribute.rs +++ b/minifi_rust/minifi_native/src/api/attribute.rs @@ -25,4 +25,8 @@ pub struct OutputAttribute { pub trait GetAttribute { fn get_attribute(&self, name: &str) -> Result<Option<String>, MinifiError>; + fn get_required_attribute(&self, name: &str) -> Result<String, MinifiError> { + self.get_attribute(name)? + .ok_or(MinifiError::missing_required_attribute(name.to_owned())) + } } diff --git a/minifi_rust/minifi_native/src/api/errors.rs b/minifi_rust/minifi_native/src/api/errors.rs index 633d413ad..2c833a997 100644 --- a/minifi_rust/minifi_native/src/api/errors.rs +++ b/minifi_rust/minifi_native/src/api/errors.rs @@ -17,9 +17,10 @@ use minifi_native_sys::minifi_status; use std::borrow::Cow; +use std::error::Error; use std::ffi::NulError; use std::fmt; -use std::num::{NonZeroU32, ParseIntError}; +use std::num::{NonZeroU32, ParseFloatError, ParseIntError}; use std::str::ParseBoolError; #[derive(Debug, Clone)] @@ -30,6 +31,7 @@ pub enum ParseError { Duration(humantime::DurationError), Size(byte_unit::ParseError), Nul(NulError), + Float(ParseFloatError), Other, } @@ -37,6 +39,7 @@ pub enum ParseError { pub enum MinifiError { UnknownError, StatusError((Cow<'static, str>, NonZeroU32)), + MissingRequiredAttribute(Cow<'static, str>), MissingRequiredProperty(Cow<'static, str>), ControllerServiceError(Cow<'static, str>), ValidationError(Cow<'static, str>), @@ -89,6 +92,18 @@ impl From<NulError> for MinifiError { } } +impl From<ParseFloatError> for MinifiError { + fn from(err: ParseFloatError) -> Self { + MinifiError::Parse(ParseError::Float(err)) + } +} + +impl From<std::convert::Infallible> for MinifiError { + fn from(_: std::convert::Infallible) -> Self { + unreachable!("Infallible errors can never happen") + } +} + impl MinifiError { pub(crate) fn to_status(&self) -> minifi_status { match self { @@ -125,9 +140,17 @@ impl MinifiError { MinifiError::MissingRequiredProperty(msg.into()) } + pub fn missing_required_attribute<S: Into<Cow<'static, str>>>(msg: S) -> Self { + MinifiError::MissingRequiredAttribute(msg.into()) + } + pub fn controller_service_err<S: Into<Cow<'static, str>>>(msg: S) -> Self { MinifiError::ControllerServiceError(msg.into()) } + + pub fn parse_err() -> Self { + MinifiError::Parse(ParseError::Other) + } } impl fmt::Display for MinifiError { @@ -158,3 +181,5 @@ impl fmt::Display for MinifiError { } } } + +impl Error for MinifiError {} diff --git a/minifi_rust/minifi_native/src/api/logger.rs b/minifi_rust/minifi_native/src/api/logger.rs index b7df51817..80a20b6f6 100644 --- a/minifi_rust/minifi_native/src/api/logger.rs +++ b/minifi_rust/minifi_native/src/api/logger.rs @@ -18,9 +18,23 @@ use std::fmt; use std::fmt::Debug; -use strum_macros::{Display, EnumString, VariantNames}; +use minifi_native_macros::PropertyType; +use strum_macros::{Display, EnumString, IntoStaticStr, VariantNames}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Display, EnumString, VariantNames)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Display, + EnumString, + VariantNames, + IntoStaticStr, + PropertyType, +)] #[strum(serialize_all = "PascalCase", const_into_str)] pub enum LogLevel { Trace, diff --git a/minifi_rust/minifi_native/src/api/process_context.rs b/minifi_rust/minifi_native/src/api/process_context.rs index 0f114cf47..528dceb42 100644 --- a/minifi_rust/minifi_native/src/api/process_context.rs +++ b/minifi_rust/minifi_native/src/api/process_context.rs @@ -15,7 +15,6 @@ // specific language governing permissions and limitations // under the License. -use crate::StandardPropertyValidator::*; use crate::api::RawControllerService; use crate::api::component_definition_traits::ComponentIdentifier; use crate::api::flow_file::FlowFile; @@ -24,8 +23,6 @@ use crate::{ ControllerServiceApi, ControllerServiceDefinition, EnableControllerService, GetProperty, MinifiError, Property, }; -use std::str::FromStr; -use std::time::Duration; pub trait ProcessContext { type FlowFile: FlowFile; @@ -36,80 +33,6 @@ pub trait ProcessContext { flow_file: Option<&Self::FlowFile>, ) -> Result<Option<String>, MinifiError>; - fn get_bool_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result<Option<bool>, MinifiError> { - if property.validator != BoolValidator { - return Err(MinifiError::validation_err(format!( - "to use get_bool_property {:?} must have BoolValidator", - property - ))); - } - - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(bool::from_str(&property_val)?)) - } else { - Ok(None) - } - } - - fn get_duration_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result<Option<Duration>, MinifiError> { - if property.validator != TimePeriodValidator { - return Err(MinifiError::validation_err(format!( - "to use get_duration_property {:?} must have TimePeriodValidator", - property - ))); - } - - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(humantime::parse_duration(property_val.as_str())?)) - } else { - Ok(None) - } - } - - fn get_size_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result<Option<u64>, MinifiError> { - if property.validator != DataSizeValidator { - return Err(MinifiError::validation_err(format!( - "to use get_size_property {:?} must have DataSizeValidator", - property - ))); - } - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(byte_unit::Byte::from_str(&property_val)?.as_u64())) - } else { - Ok(None) - } - } - - fn get_u64_property( - &self, - property: &Property, - flow_file: Option<&Self::FlowFile>, - ) -> Result<Option<u64>, MinifiError> { - if property.validator != U64Validator { - return Err(MinifiError::validation_err(format!( - "to use get_u64_property {:?} must have U64Validator", - property - ))); - } - if let Some(property_val) = self.get_property(property, flow_file)? { - Ok(Some(u64::from_str(&property_val)?)) - } else { - Ok(None) - } - } - fn get_raw_controller_service<Cs>( &self, property: &Property, @@ -133,7 +56,7 @@ impl<S> GetProperty for S where S: ProcessContext, { - fn get_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { self.get_property(property, None) } } diff --git a/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs b/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs index f856ca83d..797542dcd 100644 --- a/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs +++ b/minifi_rust/minifi_native/src/api/processor_wrappers/flow_file_transform.rs @@ -67,6 +67,19 @@ impl<'a> TransformedFlowFile<'a> { pub fn attributes_to_add(&self) -> &HashMap<String, String> { &self.attributes_to_add } + + #[cfg(any(test, feature = "test-utils"))] + pub fn into_bytes(self) -> std::io::Result<Option<Vec<u8>>> { + match self.new_content { + Some(Content::Buffer(vec)) => Ok(Some(vec)), + Some(Content::Stream(mut stream)) => { + let mut buffer = Vec::new(); + stream.read_to_end(&mut buffer)?; + Ok(Some(buffer)) + } + None => Ok(None), + } + } } pub trait FlowFileTransform { @@ -200,3 +213,42 @@ where } } } + +#[macro_export] +macro_rules! unwrap_or_route { + ($result:expr, $route:expr) => { + match $result { + Ok(v) => v, + Err(e) => { + return Ok(TransformedFlowFile::route_without_changes($route)); + } + } + }; + + ($result:expr, $route:expr, $custom_logger:expr) => { + match $result { + Ok(v) => v, + Err(e) => { + minifi_native::error!( + $custom_logger, + "Failed to unwrap due to {}. Routing flow file.", + e + ); + return Ok(TransformedFlowFile::route_without_changes($route)); + } + } + }; + + ($result:expr, $route:expr, $custom_logger:expr, $context:expr) => { + match $result { + Ok(v) => v, + Err(e) => { + error!( + $custom_logger, + "Failed to {} due to {}. Routing flow file.", $context, e + ); + return Ok(TransformedFlowFile::route_without_changes($route)); + } + } + }; +} diff --git a/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs b/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs index 8745b2fb7..c05942066 100644 --- a/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs +++ b/minifi_rust/minifi_native/src/api/processor_wrappers/utils/context_session_flowfile_bundle.rs @@ -55,7 +55,7 @@ where PC: ProcessContext, PS: ProcessSession<FlowFile = PC::FlowFile>, { - fn get_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { self.context.get_property(property, self.flow_file) } } diff --git a/minifi_rust/minifi_native/src/api/property.rs b/minifi_rust/minifi_native/src/api/property.rs index 952b6eb68..52b2adb31 100644 --- a/minifi_rust/minifi_native/src/api/property.rs +++ b/minifi_rust/minifi_native/src/api/property.rs @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +use crate::PropertyConstraints::NoConstraints; use crate::StandardPropertyValidator::{ BoolValidator, DataSizeValidator, TimePeriodValidator, U64Validator, }; @@ -26,7 +27,6 @@ use std::time::Duration; #[derive(Debug, Eq, PartialEq)] pub enum StandardPropertyValidator { - AlwaysValidValidator, NonBlankValidator, TimePeriodValidator, BoolValidator, @@ -36,6 +36,20 @@ pub enum StandardPropertyValidator { PortValidator, } +#[derive(Debug, PartialEq)] +pub enum PropertyConstraints { + NoConstraints, + Validator(StandardPropertyValidator), + AllowedValues(&'static [&'static str]), + AllowedType(&'static str), +} + +impl From<StandardPropertyValidator> for PropertyConstraints { + fn from(value: StandardPropertyValidator) -> Self { + PropertyConstraints::Validator(value) + } +} + #[derive(Debug)] pub struct Property { pub name: &'static str, @@ -44,69 +58,100 @@ pub struct Property { pub is_sensitive: bool, pub supports_expr_lang: bool, pub default_value: Option<&'static str>, - pub validator: StandardPropertyValidator, - pub allowed_values: &'static [&'static str], - pub allowed_type: Option<&'static str>, + pub constraints: PropertyConstraints, } -pub trait GetProperty { - fn get_property(&self, property: &Property) -> Result<Option<String>, MinifiError>; - fn get_bool_property(&self, property: &Property) -> Result<Option<bool>, MinifiError> { - if property.validator != BoolValidator { - return Err(MinifiError::validation_err(format!( - "to use get_bool_property {:?} must have BoolValidator", - property - ))); - } +pub trait PropertyType { + type Output; + const EXPECTED_CONSTRAINTS: PropertyConstraints = PropertyConstraints::NoConstraints; - if let Some(property_val) = self.get_property(property)? { - Ok(Some(bool::from_str(&property_val)?)) - } else { - Ok(None) - } - } + fn parse(s: &str) -> Result<Self::Output, MinifiError>; +} - fn get_duration_property(&self, property: &Property) -> Result<Option<Duration>, MinifiError> { - if property.validator != TimePeriodValidator { - return Err(MinifiError::validation_err(format!( - "to use get_duration_property {:?} must have TimePeriodValidator", - property - ))); - } +macro_rules! impl_from_str_property { + ($t:ty, $validator:expr) => { + impl PropertyType for $t { + type Output = $t; + const EXPECTED_CONSTRAINTS: PropertyConstraints = $validator; - if let Some(property_val) = self.get_property(property)? { - Ok(Some(humantime::parse_duration(property_val.as_str())?)) - } else { - Ok(None) + fn parse(s: &str) -> Result<Self::Output, MinifiError> { + s.parse::<$t>().map_err(Into::into) + } } + }; + ($t:ty) => { + impl_from_str_property!($t, PropertyConstraints::NoConstraints); + }; +} + +impl_from_str_property!(String); +impl_from_str_property!(std::path::PathBuf); +impl_from_str_property!(f64); +impl_from_str_property!(f32); +impl_from_str_property!(i64); +impl_from_str_property!(i32); +impl_from_str_property!(bool, PropertyConstraints::Validator(BoolValidator)); +impl_from_str_property!(u64, PropertyConstraints::Validator(U64Validator)); +impl_from_str_property!(u32, PropertyConstraints::Validator(U64Validator)); +impl_from_str_property!(usize, PropertyConstraints::Validator(U64Validator)); + +impl PropertyType for Duration { + type Output = Duration; + const EXPECTED_CONSTRAINTS: PropertyConstraints = + PropertyConstraints::Validator(TimePeriodValidator); + fn parse(s: &str) -> Result<Self::Output, MinifiError> { + humantime::parse_duration(s).map_err(Into::into) + } +} + +pub struct DataSize; +impl PropertyType for DataSize { + type Output = u64; + const EXPECTED_CONSTRAINTS: PropertyConstraints = + PropertyConstraints::Validator(DataSizeValidator); + fn parse(s: &str) -> Result<Self::Output, MinifiError> { + byte_unit::Byte::from_str(s) + .map(|b| b.as_u64()) + .map_err(Into::into) } +} + +pub trait GetProperty { + fn get_raw_property(&self, property: &Property) -> Result<Option<String>, MinifiError>; - fn get_size_property(&self, property: &Property) -> Result<Option<u64>, MinifiError> { - if property.validator != DataSizeValidator { + fn get_property<T: PropertyType>( + &self, + property: &Property, + ) -> Result<Option<T::Output>, MinifiError> { + if T::EXPECTED_CONSTRAINTS != NoConstraints + && T::EXPECTED_CONSTRAINTS != property.constraints + { return Err(MinifiError::validation_err(format!( - "to use get_size_property {:?} must have DataSizeValidator", - property + "to use get_property for this type, {:?} must have validator {:?}", + property.name, + T::EXPECTED_CONSTRAINTS ))); } - if let Some(property_val) = self.get_property(property)? { - Ok(Some(byte_unit::Byte::from_str(&property_val)?.as_u64())) + + if let Some(property_val) = self.get_raw_property(property)? { + Ok(Some(T::parse(&property_val)?)) } else { Ok(None) } } - fn get_u64_property(&self, property: &Property) -> Result<Option<u64>, MinifiError> { - if property.validator != U64Validator { + fn get_req_property<T: PropertyType>( + &self, + property: &Property, + ) -> Result<T::Output, MinifiError> { + if !property.is_required { return Err(MinifiError::validation_err(format!( - "to use get_u64_property {:?} must have U64Validator", - property + "to use get_req_property, {:?} must be required", + property.name ))); } - if let Some(property_val) = self.get_property(property)? { - Ok(Some(u64::from_str(&property_val)?)) - } else { - Ok(None) - } + self.get_property::<T>(property)? + .ok_or_else(|| MinifiError::missing_required_property(property.name)) } } diff --git a/minifi_rust/minifi_native/src/api/relationship.rs b/minifi_rust/minifi_native/src/api/relationship.rs index 45041a2ba..df3e34081 100644 --- a/minifi_rust/minifi_native/src/api/relationship.rs +++ b/minifi_rust/minifi_native/src/api/relationship.rs @@ -15,8 +15,16 @@ // specific language governing permissions and limitations // under the License. +use std::fmt::{Display, Formatter}; + #[derive(Debug, Eq, PartialEq)] pub struct Relationship { pub name: &'static str, pub description: &'static str, } + +impl Display for Relationship { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} diff --git a/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs b/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs index 2284c8dc7..2c285fd4f 100644 --- a/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs +++ b/minifi_rust/minifi_native/src/c_ffi/c_ffi_controller_service_context.rs @@ -61,7 +61,7 @@ unsafe extern "C" fn property_callback( } impl<'a> GetProperty for CffiControllerServiceContext<'a> { - fn get_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { let mut result: Option<String> = None; let property_name: StringView = StringView::new(property.name); diff --git a/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs b/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs index d2fe2c1b7..05d23b0c2 100644 --- a/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs +++ b/minifi_rust/minifi_native/src/c_ffi/c_ffi_property.rs @@ -16,6 +16,7 @@ // under the License. use super::c_ffi_primitives::StaticStrAsMinifiCStr; +use crate::api::property::PropertyConstraints; use crate::{Property, StandardPropertyValidator}; use minifi_native_sys::{ minifi_property_definition, minifi_string_view, minifi_validator, @@ -76,11 +77,14 @@ impl Property { fn create_c_allowed_values_vec_vec(properties: &[Self]) -> Vec<Vec<minifi_string_view>> { properties .iter() - .map(|p| { - p.allowed_values + .map(|p| match p.constraints { + PropertyConstraints::AllowedValues(allowed_values) => allowed_values .iter() .map(|av| av.as_minifi_c_type()) - .collect() + .collect(), + _ => { + vec![] + } }) .collect() } @@ -88,9 +92,9 @@ impl Property { fn create_c_allowed_types_vec(properties: &[Self]) -> Vec<minifi_string_view> { properties .iter() - .map(|p| match p.allowed_type { - Some(dv) => dv.as_minifi_c_type(), - None => minifi_string_view { + .map(|p| match p.constraints { + PropertyConstraints::AllowedType(allowed_type) => allowed_type.as_minifi_c_type(), + _ => minifi_string_view { data: ptr::null(), length: 0, }, @@ -125,7 +129,10 @@ impl Property { }, allowed_values_count: allowed_values.len(), allowed_values_ptr: allowed_values.as_ptr(), - validator: property.validator.as_minifi_c_type(), + validator: match &property.constraints { + PropertyConstraints::Validator(s) => s.as_minifi_c_type(), + _ => minifi_validator_MINIFI_VALIDATOR_ALWAYS_VALID, + }, allowed_type: if allowed_type.data.is_null() { std::ptr::null() } else { @@ -147,9 +154,6 @@ impl Property { impl StandardPropertyValidator { pub(crate) fn as_minifi_c_type(&self) -> minifi_validator { match self { - StandardPropertyValidator::AlwaysValidValidator => { - minifi_validator_MINIFI_VALIDATOR_ALWAYS_VALID - } StandardPropertyValidator::NonBlankValidator => { minifi_validator_MINIFI_VALIDATOR_NON_BLANK } diff --git a/minifi_rust/minifi_native/src/lib.rs b/minifi_rust/minifi_native/src/lib.rs index c467b68e8..104eee006 100644 --- a/minifi_rust/minifi_native/src/lib.rs +++ b/minifi_rust/minifi_native/src/lib.rs @@ -14,6 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +extern crate self as minifi_native; mod api; pub mod c_ffi; @@ -54,8 +55,9 @@ pub use api::process_session::IoState; pub use api::attribute::{GetAttribute, OutputAttribute}; pub use api::{ - FlowFile, GetId, InputStream, OnTriggerResult, OutputStream, ProcessContext, ProcessSession, - ProcessorInputRequirement, Relationship, StandardPropertyValidator, + DataSize, FlowFile, GetId, InputStream, OnTriggerResult, OutputStream, ProcessContext, + ProcessSession, ProcessorInputRequirement, PropertyConstraints, PropertyType, Relationship, + StandardPropertyValidator, }; pub use minifi_native_macros as macros; diff --git a/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs b/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs index 2a594ef8a..f67bd5008 100644 --- a/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs +++ b/minifi_rust/minifi_native/src/mock/mock_controller_service_context.rs @@ -23,7 +23,7 @@ pub struct MockControllerServiceContext { } impl GetProperty for MockControllerServiceContext { - fn get_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { + fn get_raw_property(&self, property: &Property) -> Result<Option<String>, MinifiError> { self.properties.get_property(property, None) } } diff --git a/minifi_rust/minifi_native_macros/src/lib.rs b/minifi_rust/minifi_native_macros/src/lib.rs index fa8e56336..10f24656d 100644 --- a/minifi_rust/minifi_native_macros/src/lib.rs +++ b/minifi_rust/minifi_native_macros/src/lib.rs @@ -52,3 +52,24 @@ pub fn controller_service_api(_attr: TokenStream, item: TokenStream) -> TokenStr TokenStream::from(expanded) } + +#[proc_macro_derive(PropertyType)] +pub fn derive_property_type(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; + + let expanded = quote! { + impl ::minifi_native::PropertyType for #name { + type Output = #name; + const EXPECTED_CONSTRAINTS: ::minifi_native::PropertyConstraints = + ::minifi_native::PropertyConstraints::AllowedValues( + <#name as ::strum::VariantNames>::VARIANTS + ); + fn parse(s: &str) -> Result<Self::Output, ::minifi_native::MinifiError> { + s.parse::<#name>().map_err(Into::into) + } + } + }; + + TokenStream::from(expanded) +} diff --git a/minifi_rust/minifi_rs_behave/Dockerfile.alpine b/minifi_rust/minifi_rs_behave/Dockerfile.alpine index 032c26ec5..dd0cd8a40 100644 --- a/minifi_rust/minifi_rs_behave/Dockerfile.alpine +++ b/minifi_rust/minifi_rs_behave/Dockerfile.alpine @@ -23,3 +23,4 @@ RUN cargo build --release # Export Stage FROM scratch AS bin-export COPY --from=builder /app/target/release/libminifi_rs_playground.so / +COPY --from=builder /app/target/release/libminifi_tensor.so / diff --git a/minifi_rust/minifi_rs_behave/Dockerfile.debian b/minifi_rust/minifi_rs_behave/Dockerfile.debian index 274ed3cb5..db44c5b49 100644 --- a/minifi_rust/minifi_rs_behave/Dockerfile.debian +++ b/minifi_rust/minifi_rs_behave/Dockerfile.debian @@ -1,5 +1,8 @@ -FROM rust:slim-bullseye AS chef -RUN apt-get update && apt-get install -y clang lld pkg-config curl tar && cargo install cargo-chef +FROM rust:slim-bookworm AS chef +RUN apt update && apt install -y wget software-properties-common gnupg sudo +RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor -o /usr/share/keyrings/llvm-snapshot.gpg +RUN echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg] http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-18 main" | tee /etc/apt/sources.list.d/llvm-toolchain-18.list +RUN apt update && apt install -y clang-18 lld-18 clangd-18 libclang-18-dev lld pkg-config curl tar && cargo install cargo-chef WORKDIR /app FROM chef AS planner @@ -23,3 +26,4 @@ RUN cargo build --release # Export Stage FROM scratch AS bin-export COPY --from=builder /app/target/release/libminifi_rs_playground.so / +COPY --from=builder /app/target/release/libminifi_tensor.so / diff --git a/minifi_rust/minifi_rs_behave/src/main.rs b/minifi_rust/minifi_rs_behave/src/main.rs index f0d8c12bc..2e8517f09 100644 --- a/minifi_rust/minifi_rs_behave/src/main.rs +++ b/minifi_rust/minifi_rs_behave/src/main.rs @@ -95,6 +95,7 @@ impl BehaveRunner { .arg("pip") .arg("install") .arg(self.minifi_behave_path.to_string_lossy().as_ref()) + .arg("certifi") .status() .expect("Failed to install dependencies") }
