use std::env;
pub fn expand_env_vars_in_string(value: &str) -> (String, Vec<String>) {
let mut missing_vars = Vec::new();
let mut result = String::new();
let mut chars = value.chars().peekable();
while let Some(c) = chars.next() {
if c == '$' && chars.peek() == Some(&'{') {
chars.next(); let mut var_content = String::new();
while let Some(ch) = chars.next() {
if ch == '}' {
break;
}
var_content.push(ch);
}
let parts: Vec<&str> = var_content.splitn(2, ":-").collect();
let var_name = parts[0];
let default_value = parts.get(1);
if let Ok(env_value) = env::var(var_name) {
result.push_str(&env_value);
} else if let Some(default) = default_value {
result.push_str(default);
} else {
missing_vars.push(var_name.to_string());
result.push_str(&format!("${{{}}}", var_content));
}
} else {
result.push(c);
}
}
(result, missing_vars)
}
pub fn expand_env_vars_in_map(
env: &std::collections::HashMap<String, String>,
) -> (std::collections::HashMap<String, String>, Vec<String>) {
let mut missing_vars = Vec::new();
let mut result = std::collections::HashMap::new();
for (key, value) in env {
let (expanded, mut missing) = expand_env_vars_in_string(value);
missing_vars.append(&mut missing);
result.insert(key.clone(), expanded);
}
(result, missing_vars)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_expand_env_vars_simple() {
unsafe { env::set_var("TEST_VAR", "test_value") };
let (result, missing) = expand_env_vars_in_string("prefix_${TEST_VAR}_suffix");
assert_eq!(result, "prefix_test_value_suffix");
assert!(missing.is_empty());
unsafe { env::remove_var("TEST_VAR") };
}
#[test]
fn test_expand_env_vars_with_default() {
let (result, missing) = expand_env_vars_in_string("value is ${NONEXISTENT:-default_val}");
assert_eq!(result, "value is default_val");
assert!(missing.is_empty());
}
#[test]
fn test_expand_env_vars_missing() {
let (result, missing) = expand_env_vars_in_string("value is ${MISSING_VAR}");
assert_eq!(result, "value is ${MISSING_VAR}");
assert_eq!(missing, vec!["MISSING_VAR"]);
}
#[test]
fn test_expand_env_vars_in_map() {
unsafe { env::set_var("MY_VAR", "my_value") };
let mut env = std::collections::HashMap::new();
env.insert("KEY1".to_string(), "${MY_VAR}".to_string());
env.insert("KEY2".to_string(), "static".to_string());
let (result, missing) = expand_env_vars_in_map(&env);
assert_eq!(result.get("KEY1"), Some(&"my_value".to_string()));
assert_eq!(result.get("KEY2"), Some(&"static".to_string()));
assert!(missing.is_empty());
unsafe { env::remove_var("MY_VAR") };
}
}