diff --git a/Cargo.lock b/Cargo.lock index b218871..b6c621b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -595,6 +595,7 @@ name = "code0-flow" version = "0.0.0" dependencies = [ "async-trait", + "dotenv", "futures-lite 2.6.0", "lapin", "log", @@ -831,6 +832,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "either" version = "1.15.0" diff --git a/Cargo.toml b/Cargo.toml index 66dffa4..771335a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] version = "0.0.0" name = "code0-flow" -edition = "2021" +edition = "2024" description = "Crate for managing the code0-flows inside of the Flow Queue & FlowStore" repository = "https://github.com/code0-tech/code0-flow" homepage = "https://code0.tech" @@ -23,6 +23,8 @@ serde = "1.0.138" lapin = "2.5.0" futures-lite = "2.6.0" tonic = "0.13.0" +dotenv = "0.15.0" + [dev-dependencies] testcontainers = "0.24.0" @@ -36,4 +38,5 @@ default = ["all"] flow_queue = [] flow_store = [] flow_definition = [] -all = ["flow_queue", "flow_store", "flow_definition"] +flow_config = [] +all = ["flow_queue", "flow_store", "flow_definition", "flow_config"] diff --git a/src/flow_config/environment.rs b/src/flow_config/environment.rs new file mode 100644 index 0000000..07c403e --- /dev/null +++ b/src/flow_config/environment.rs @@ -0,0 +1,21 @@ +use std::str::FromStr; + +#[derive(Debug, PartialEq, Eq)] +pub enum Environment { + Development, + Staging, + Production, +} + +impl FromStr for Environment { + type Err = (); + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "staging" => Ok(Environment::Staging), + "production" => Ok(Environment::Production), + "development" => Ok(Environment::Development), + _ => Ok(Environment::Development), + } + } +} diff --git a/src/flow_config/mod.rs b/src/flow_config/mod.rs new file mode 100644 index 0000000..2d53dbd --- /dev/null +++ b/src/flow_config/mod.rs @@ -0,0 +1,256 @@ +use std::{fmt::Debug, str::FromStr}; + +pub mod environment; +pub mod mode; + +pub fn env_with_default(key: &str, default: T) -> T { + match std::env::var(key) { + Ok(string) => match T::from_str(&string) { + Ok(value) => { + log::info!("Found env: {} with value: {:?}", key, &value); + value + } + Err(_) => { + log::warn!("Failed to parse env: {} with value: {:?}", key, &string); + default + } + }, + Err(_) => { + log::warn!("Failed to find env: {}", key); + default + } + } +} + +pub fn load_env_file() { + match dotenv::dotenv() { + Ok(path) => log::info!("Found Env. file at {:?} ", path), + Err(e) => log::error!("Failed to load .env file. Reason: {:?}", e), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serial_test::serial; + use std::env; + + #[test] + #[serial] + fn test_env_with_default_string_exists() { + let key = "TEST_STRING_VAR"; + let expected = "test_value"; + + unsafe { + env::set_var(key, expected); + } + + let result = env_with_default(key, "default".to_string()); + assert_eq!(result, expected); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_string_missing() { + let key = "TEST_MISSING_STRING_VAR"; + unsafe { + env::remove_var(key); + } + + let default = "default_value".to_string(); + let result = env_with_default(key, default.clone()); + assert_eq!(result, default); + } + + #[test] + #[serial] + fn test_env_with_default_integer_exists() { + let key = "TEST_INT_VAR"; + let expected = 42; + unsafe { + env::set_var(key, expected.to_string()); + } + + let result = env_with_default(key, 0i32); + assert_eq!(result, expected); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_integer_missing() { + let key = "TEST_MISSING_INT_VAR"; + unsafe { + env::remove_var(key); + } + + let default = 123i32; + let result = env_with_default(key, default); + assert_eq!(result, default); + } + + #[test] + #[serial] + fn test_env_with_default_boolean_exists_true() { + let key = "TEST_BOOL_TRUE_VAR"; + unsafe { + env::set_var(key, "true"); + } + + let result = env_with_default(key, false); + assert_eq!(result, true); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_boolean_exists_false() { + let key = "TEST_BOOL_FALSE_VAR"; + unsafe { + env::set_var(key, "false"); + } + + let result = env_with_default(key, true); + assert_eq!(result, false); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_boolean_missing() { + let key = "TEST_MISSING_BOOL_VAR"; + unsafe { + env::remove_var(key); + } + + let default = true; + let result = env_with_default(key, default); + assert_eq!(result, default); + } + + #[test] + #[serial] + fn test_env_with_default_boolean_invalid() { + let key = "TEST_INVALID_BOOL_VAR"; + unsafe { + env::set_var(key, "maybe"); + } + + let result = env_with_default(key, false); + assert_eq!(result, false); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_u32_exists() { + let key = "TEST_U32_VAR"; + let expected = 42u32; + unsafe { + env::set_var(key, expected.to_string()); + } + + let result = env_with_default(key, 0u32); + assert_eq!(result, expected); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_u32_negative_invalid() { + let key = "TEST_U32_NEGATIVE_VAR"; + unsafe { + env::set_var(key, "-42"); + } + + let result = env_with_default(key, 0u32); + assert_eq!(result, 0u32); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_empty_string() { + let key = "TEST_EMPTY_STRING_VAR"; + unsafe { + env::set_var(key, ""); + } + + let result = env_with_default(key, "default".to_string()); + assert_eq!(result, ""); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_default_whitespace_string() { + let key = "TEST_WHITESPACE_VAR"; + unsafe { + env::set_var(key, " whitespace "); + } + + let result = env_with_default(key, "default".to_string()); + assert_eq!(result, " whitespace "); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_environment() { + let key = "TEST_ENVIRONMENT"; + unsafe { + env::set_var(key, "DEVELOPMENT"); + } + + let result = env_with_default(key, environment::Environment::Development); + assert_eq!(result, environment::Environment::Development); + + unsafe { + env::remove_var(key); + } + } + + #[test] + #[serial] + fn test_env_with_mode() { + let key = "TEST_MODE"; + unsafe { + env::set_var(key, "STATIC"); + } + + let result = env_with_default(key, mode::Mode::STATIC); + assert_eq!(result, mode::Mode::STATIC); + + unsafe { + env::remove_var(key); + } + } +} diff --git a/src/flow_config/mode.rs b/src/flow_config/mode.rs new file mode 100644 index 0000000..09c2306 --- /dev/null +++ b/src/flow_config/mode.rs @@ -0,0 +1,24 @@ +use std::str::FromStr; + +/// STATIC: +/// The service will start with no Sagittarius in mind +/// +/// DYNAMIC: +/// The service will start with Sagittarius in mind +#[derive(PartialEq, Eq, Debug)] +pub enum Mode { + STATIC, + DYNAMIC, +} + +impl FromStr for Mode { + type Err = (); + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "static" => Ok(Mode::STATIC), + "dynamic" => Ok(Mode::DYNAMIC), + _ => Ok(Mode::STATIC), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index a1a484d..95fcfef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,6 @@ pub mod flow_queue; #[cfg(feature = "flow_definition")] pub mod flow_definition; + +#[cfg(feature = "flow_config")] +pub mod flow_config; \ No newline at end of file