use std::collections::HashMap;
#[must_use]
pub fn get(env: &[(String, String)], name: &str) -> Option<String> {
env.iter().find(|(k, _)| k == name).map(|(_, v)| v.clone())
}
#[must_use]
pub fn get_or(env: &[(String, String)], name: &str, default: &str) -> String {
get(env, name).unwrap_or_else(|| default.to_string())
}
#[must_use]
pub fn bool(env: &[(String, String)], name: &str, default: bool) -> bool {
get(env, name).map_or(default, |v| {
let v_lower = v.to_lowercase();
v_lower == "true" || v_lower == "1" || v_lower == "yes"
})
}
#[must_use]
pub fn all(env: &[(String, String)]) -> Vec<(String, String)> {
env.to_vec()
}
#[derive(Debug)]
pub struct EnvCache {
map: HashMap<String, String>,
vec: Vec<(String, String)>,
}
impl EnvCache {
#[must_use]
pub fn new(env: Vec<(String, String)>) -> Self {
let map = env.iter().cloned().collect();
Self { map, vec: env }
}
#[must_use]
pub fn get(&self, name: &str) -> Option<String> {
self.map.get(name).cloned()
}
#[must_use]
pub fn get_or(&self, name: &str, default: &str) -> String {
self.map
.get(name)
.cloned()
.unwrap_or_else(|| default.to_string())
}
#[must_use]
pub fn bool(&self, name: &str, default: bool) -> bool {
self.map.get(name).map_or(default, |v| {
let v_lower = v.to_lowercase();
v_lower == "true" || v_lower == "1" || v_lower == "yes"
})
}
#[inline]
#[must_use]
pub fn all(&self) -> &[(String, String)] {
&self.vec
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mock_env() -> Vec<(String, String)> {
vec![
("PORT".to_string(), "3000".to_string()),
("HOST".to_string(), "localhost".to_string()),
("DEBUG".to_string(), "true".to_string()),
("VERBOSE".to_string(), "1".to_string()),
("QUIET".to_string(), "yes".to_string()),
("ENABLED".to_string(), "false".to_string()),
]
}
#[test]
fn test_get() {
let env = mock_env();
assert_eq!(get(&env, "PORT"), Some("3000".to_string()));
assert_eq!(get(&env, "HOST"), Some("localhost".to_string()));
assert_eq!(get(&env, "NONEXISTENT"), None);
}
#[test]
fn test_get_or() {
let env = mock_env();
assert_eq!(get_or(&env, "PORT", "8080"), "3000");
assert_eq!(get_or(&env, "NONEXISTENT", "default"), "default");
}
#[test]
fn test_bool() {
let env = mock_env();
assert!(bool(&env, "DEBUG", false));
assert!(bool(&env, "VERBOSE", false));
assert!(bool(&env, "QUIET", false));
assert!(!bool(&env, "ENABLED", true));
assert!(!bool(&env, "NONEXISTENT", false));
assert!(bool(&env, "NONEXISTENT", true));
}
#[test]
fn test_bool_case_insensitive() {
let env = vec![
("TRUE_UPPER".to_string(), "TRUE".to_string()),
("TRUE_LOWER".to_string(), "true".to_string()),
("TRUE_MIXED".to_string(), "TrUe".to_string()),
("YES_UPPER".to_string(), "YES".to_string()),
("ONE".to_string(), "1".to_string()),
];
assert!(bool(&env, "TRUE_UPPER", false));
assert!(bool(&env, "TRUE_LOWER", false));
assert!(bool(&env, "TRUE_MIXED", false));
assert!(bool(&env, "YES_UPPER", false));
assert!(bool(&env, "ONE", false));
}
#[test]
fn test_all() {
let env = mock_env();
let all_vars = all(&env);
assert_eq!(all_vars.len(), 6);
assert!(all_vars.contains(&("PORT".to_string(), "3000".to_string())));
assert!(all_vars.contains(&("DEBUG".to_string(), "true".to_string())));
}
#[test]
fn test_env_cache() {
let cache = EnvCache::new(mock_env());
assert_eq!(cache.get("PORT"), Some("3000".to_string()));
assert_eq!(cache.get_or("PORT", "8080"), "3000");
assert_eq!(cache.get_or("NONEXISTENT", "default"), "default");
assert!(cache.bool("DEBUG", false));
assert!(!cache.bool("NONEXISTENT", false));
assert_eq!(cache.all().len(), 6);
}
}