use std::sync::LazyLock;
use regex::Regex;
#[expect(
clippy::expect_used,
reason = "compile-time-constant regex; failure is a programmer bug, not runtime input"
)]
static INTERPOLATION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"\$\{([^}:]+)(?::-(.*?))?\}")
.expect("INTERPOLATION_REGEX is a valid regex - this is a compile-time constant")
});
#[must_use]
pub fn read_env_optional(name: &str) -> Option<String> {
match std::env::var(name) {
Ok(v) if !v.is_empty() => Some(v),
Ok(_) | Err(_) => None,
}
}
#[must_use]
pub fn contains_placeholder(input: &str) -> bool {
INTERPOLATION_REGEX.is_match(input)
}
#[must_use]
pub fn interpolate(input: &str, lookup: &impl Fn(&str) -> Option<String>) -> String {
INTERPOLATION_REGEX
.replace_all(input, |caps: ®ex::Captures| {
let full = caps[0].to_owned();
let var_name = &caps[1];
let default_value = caps.get(2).map(|m| m.as_str());
lookup(var_name).unwrap_or_else(|| default_value.map_or(full, str::to_owned))
})
.into_owned()
}