use std::ffi::OsStr;
use crate::common::mix_all::ROCKETMQ_HOME_ENV;
pub struct EnvUtils;
impl EnvUtils {
pub fn get_property<K: AsRef<OsStr>>(key: K) -> Option<String> {
std::env::var(key).ok()
}
pub fn get_property_or_default<K: AsRef<OsStr>>(key: K, default: impl Into<String>) -> String {
std::env::var(key).unwrap_or_else(|_| default.into())
}
pub fn get_property_as_i32<K: AsRef<OsStr>>(key: K, default: i32) -> i32 {
std::env::var(key)
.ok()
.and_then(|v| v.parse::<i32>().ok())
.unwrap_or(default)
}
pub fn get_property_as_bool<K: AsRef<OsStr>>(key: K, default: bool) -> bool {
std::env::var(key)
.ok()
.and_then(|v| {
let lower = v.to_lowercase();
match lower.as_str() {
"true" | "1" => Some(true),
"false" | "0" => Some(false),
_ => None,
}
})
.unwrap_or(default)
}
pub fn get_rocketmq_home() -> String {
std::env::var(ROCKETMQ_HOME_ENV).unwrap_or_else(|_| {
std::env::current_dir()
.ok()
.and_then(|path| path.into_os_string().into_string().ok())
.unwrap_or_else(|| ".".to_string())
})
}
}
#[cfg(test)]
mod tests {
use std::ffi::OsString;
use std::sync::Mutex;
use super::*;
static ENV_LOCK: Mutex<()> = Mutex::new(());
struct EnvironmentRestore {
key: &'static str,
original: Option<OsString>,
}
impl Drop for EnvironmentRestore {
fn drop(&mut self) {
unsafe {
match &self.original {
Some(value) => std::env::set_var(self.key, value),
None => std::env::remove_var(self.key),
}
}
}
}
fn with_environment<R>(key: &'static str, value: Option<&str>, test: impl FnOnce() -> R) -> R {
let _lock = ENV_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let restore = EnvironmentRestore {
key,
original: std::env::var_os(key),
};
unsafe {
match value {
Some(value) => std::env::set_var(key, value),
None => std::env::remove_var(key),
}
}
let result = test();
drop(restore);
result
}
#[test]
fn test_get_property_existing_variable() {
with_environment("ROCKETMQ_ENV_UTILS_EXISTING", Some("expected"), || {
assert_eq!(
EnvUtils::get_property("ROCKETMQ_ENV_UTILS_EXISTING"),
Some("expected".to_string())
);
});
}
#[test]
fn test_get_property_non_existing_variable() {
with_environment("ROCKETMQ_ENV_UTILS_MISSING", None, || {
assert_eq!(EnvUtils::get_property("ROCKETMQ_ENV_UTILS_MISSING"), None);
});
}
#[test]
fn rocketmq_home_fallback_does_not_write_the_environment() {
with_environment(ROCKETMQ_HOME_ENV, None, || {
let result = EnvUtils::get_rocketmq_home();
assert_eq!(result, std::env::current_dir().unwrap().to_string_lossy());
assert_eq!(std::env::var_os(ROCKETMQ_HOME_ENV), None);
});
}
#[test]
fn retrieves_env_variable_value() {
with_environment("ROCKETMQ_ENV_UTILS_STRING", Some("test_value"), || {
assert_eq!(
EnvUtils::get_property_or_default("ROCKETMQ_ENV_UTILS_STRING", "default_value"),
"test_value"
);
});
}
#[test]
fn returns_default_when_env_variable_not_set() {
assert_eq!(
EnvUtils::get_property_or_default("NON_EXISTENT_KEY", "default_value"),
"default_value"
);
}
#[test]
fn retrieves_env_variable_as_i32() {
with_environment("ROCKETMQ_ENV_UTILS_INT", Some("42"), || {
assert_eq!(EnvUtils::get_property_as_i32("ROCKETMQ_ENV_UTILS_INT", 0), 42);
});
}
#[test]
fn returns_default_when_env_variable_as_i32_not_set() {
assert_eq!(EnvUtils::get_property_as_i32("NON_EXISTENT_INT_KEY", 10), 10);
}
#[test]
fn returns_default_when_env_variable_as_i32_invalid() {
with_environment("ROCKETMQ_ENV_UTILS_INVALID_INT", Some("not_a_number"), || {
assert_eq!(EnvUtils::get_property_as_i32("ROCKETMQ_ENV_UTILS_INVALID_INT", 5), 5);
});
}
#[test]
fn returns_default_when_env_variable_as_bool_not_set() {
assert!(EnvUtils::get_property_as_bool("NON_EXISTENT_BOOL_KEY", true));
assert!(!EnvUtils::get_property_as_bool("NON_EXISTENT_BOOL_KEY", false));
}
#[test]
fn returns_default_when_env_variable_as_bool_invalid() {
with_environment("ROCKETMQ_ENV_UTILS_INVALID_BOOL", Some("not_a_bool"), || {
assert!(EnvUtils::get_property_as_bool("ROCKETMQ_ENV_UTILS_INVALID_BOOL", true));
assert!(!EnvUtils::get_property_as_bool(
"ROCKETMQ_ENV_UTILS_INVALID_BOOL",
false
));
});
}
}