pub struct EnvGuard {
key: &'static str,
prev: Option<String>,
}
impl EnvGuard {
#[must_use]
pub fn set(key: &'static str, val: &str) -> Self {
let prev = std::env::var(key).ok();
unsafe { std::env::set_var(key, val) };
Self { key, prev }
}
#[must_use]
pub fn remove(key: &'static str) -> Self {
let prev = std::env::var(key).ok();
unsafe { std::env::remove_var(key) };
Self { key, prev }
}
pub fn with_set<F, R>(key: &'static str, val: &str, f: F) -> R
where
F: FnOnce() -> R,
{
let guard = Self::set(key, val);
let result = f();
drop(guard);
result
}
pub fn with_removed<F, R>(key: &'static str, f: F) -> R
where
F: FnOnce() -> R,
{
let guard = Self::remove(key);
let result = f();
drop(guard);
result
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
match &self.prev {
Some(v) => {
unsafe { std::env::set_var(self.key, v) }
}
None => {
unsafe { std::env::remove_var(self.key) }
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial(env)]
fn envguard_set_and_restore_when_unset() {
let key = "TEST_SUPPORT_ENVVAR_A";
EnvGuard::with_removed(key, || {
EnvGuard::with_set(key, "123", || {
assert_eq!(std::env::var(key).unwrap(), "123");
});
assert!(std::env::var(key).is_err(), "should restore to unset");
});
}
#[test]
#[serial(env)]
fn envguard_restore_previous_value() {
let key = "TEST_SUPPORT_ENVVAR_B";
EnvGuard::with_set(key, "orig", || {
EnvGuard::with_set(key, "shadow", || {
assert_eq!(std::env::var(key).unwrap(), "shadow");
});
assert_eq!(std::env::var(key).unwrap(), "orig");
});
}
#[test]
#[serial(env)]
fn envguard_remove_and_restore() {
let key = "TEST_SUPPORT_ENVVAR_C";
EnvGuard::with_set(key, "value", || {
EnvGuard::with_removed(key, || {
assert!(std::env::var(key).is_err());
});
assert_eq!(std::env::var(key).unwrap(), "value");
});
}
}