pub(crate) struct EnvVarGuard {
key: &'static str,
previous: Option<String>,
}
impl EnvVarGuard {
pub(crate) fn set(key: &'static str, value: &str) -> Self {
let previous = std::env::var(key).ok();
unsafe { std::env::set_var(key, value) };
Self { key, previous }
}
pub(crate) fn remove(key: &'static str) -> Self {
let previous = std::env::var(key).ok();
unsafe { std::env::remove_var(key) };
Self { key, previous }
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
unsafe {
match self.previous.take() {
Some(v) => std::env::set_var(self.key, v),
None => std::env::remove_var(self.key),
}
}
}
}
#[cfg(test)]
mod tests {
use super::EnvVarGuard;
use serial_test::serial;
#[test]
#[serial(dotenv_credential_env)]
fn env_var_guard_restores_prior_value() {
const KEY: &str = "TRUSTY_COMMON_ENV_GUARD_TEST_PRIOR";
unsafe { std::env::set_var(KEY, "original") };
{
let _guard = EnvVarGuard::set(KEY, "overwritten");
assert_eq!(std::env::var(KEY).as_deref(), Ok("overwritten"));
}
assert_eq!(
std::env::var(KEY).as_deref(),
Ok("original"),
"drop must restore the exact prior value"
);
unsafe { std::env::remove_var(KEY) };
}
#[test]
#[serial(dotenv_credential_env)]
fn env_var_guard_restores_absent_as_absent() {
const KEY: &str = "TRUSTY_COMMON_ENV_GUARD_TEST_ABSENT";
unsafe { std::env::remove_var(KEY) };
assert!(std::env::var(KEY).is_err(), "precondition: var is absent");
{
let _guard = EnvVarGuard::set(KEY, "temporary");
assert_eq!(std::env::var(KEY).as_deref(), Ok("temporary"));
}
assert!(
std::env::var(KEY).is_err(),
"drop must restore absence, not leave an empty or stale value"
);
}
#[test]
#[serial(dotenv_credential_env)]
fn env_var_guard_remove_then_restores() {
const KEY: &str = "TRUSTY_COMMON_ENV_GUARD_TEST_REMOVE";
unsafe { std::env::set_var(KEY, "was-here") };
{
let _guard = EnvVarGuard::remove(KEY);
assert!(std::env::var(KEY).is_err(), "guard must clear the var");
}
assert_eq!(
std::env::var(KEY).as_deref(),
Ok("was-here"),
"drop must restore the value `remove` displaced"
);
}
}