use std::ffi::OsString;
pub struct EnvVarGuard {
key: OsString,
previous_value: Option<OsString>,
}
impl EnvVarGuard {
pub fn set<K, V>(key: K, value: V) -> Self
where
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
{
let key = key.as_ref().to_os_string();
let previous_value = std::env::var_os(&key);
unsafe {
std::env::set_var(&key, value);
}
Self {
key,
previous_value,
}
}
pub fn removed<K>(key: K) -> Self
where
K: AsRef<std::ffi::OsStr>,
{
let key = key.as_ref().to_os_string();
let previous_value = std::env::var_os(&key);
unsafe {
std::env::remove_var(&key);
}
Self {
key,
previous_value,
}
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
unsafe {
match &self.previous_value {
Some(value) => std::env::set_var(&self.key, value),
None => std::env::remove_var(&self.key),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn unique_key(tag: &str) -> String {
format!("term-wm-envguard-{tag}-{}", std::process::id())
}
#[test]
#[serial_test::serial]
fn set_installs_value_and_drop_restores_original() {
let key = unique_key("set-restore");
unsafe { std::env::set_var(&key, "original") };
{
let _guard = EnvVarGuard::set(&key, "temporary");
assert_eq!(
std::env::var_os(&key).as_deref(),
Some(std::ffi::OsStr::new("temporary"))
);
}
assert_eq!(
std::env::var_os(&key).as_deref(),
Some(std::ffi::OsStr::new("original"))
);
unsafe { std::env::remove_var(&key) };
}
#[test]
#[serial_test::serial]
fn removed_unsets_and_stays_unset_after_drop_when_originally_absent() {
let key = unique_key("removed-absent");
unsafe { std::env::remove_var(&key) };
{
let _guard = EnvVarGuard::removed(&key);
assert!(std::env::var_os(&key).is_none());
}
assert!(std::env::var_os(&key).is_none(), "must stay unset");
}
#[test]
#[serial_test::serial]
fn removed_restores_previous_value_after_drop() {
let key = unique_key("removed-prev");
unsafe { std::env::set_var(&key, "keepme") };
{
let _guard = EnvVarGuard::removed(&key);
assert!(std::env::var_os(&key).is_none());
}
assert_eq!(
std::env::var_os(&key).as_deref(),
Some(std::ffi::OsStr::new("keepme"))
);
unsafe { std::env::remove_var(&key) }
}
}