#![cfg(test)]
use std::sync::{Mutex, MutexGuard};
static ENV_LOCK: Mutex<()> = Mutex::new(());
pub fn env_lock() -> MutexGuard<'static, ()> {
ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner())
}
#[must_use = "the prior env value is restored when this guard is dropped"]
pub struct ScopedEnv {
key: String,
prior: Option<String>,
}
impl ScopedEnv {
pub fn set(key: &str, value: impl AsRef<std::ffi::OsStr>) -> Self {
let prior = std::env::var(key).ok();
std::env::set_var(key, value);
Self { key: key.to_string(), prior }
}
pub fn remove(key: &str) -> Self {
let prior = std::env::var(key).ok();
std::env::remove_var(key);
Self { key: key.to_string(), prior }
}
}
impl Drop for ScopedEnv {
fn drop(&mut self) {
match &self.prior {
Some(v) => std::env::set_var(&self.key, v),
None => std::env::remove_var(&self.key),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scoped_env_set_restores_prior_absence() {
let _guard = env_lock();
let key = "PMAT_876_SCOPED_PROBE_A";
std::env::remove_var(key); {
let _s = ScopedEnv::set(key, "value");
assert_eq!(std::env::var(key).as_deref(), Ok("value"));
}
assert!(std::env::var(key).is_err());
}
#[test]
fn scoped_env_remove_restores_prior_value() {
let _guard = env_lock();
let key = "PMAT_876_SCOPED_PROBE_B";
std::env::set_var(key, "original");
{
let _s = ScopedEnv::remove(key);
assert!(std::env::var(key).is_err());
}
assert_eq!(std::env::var(key).as_deref(), Ok("original"));
std::env::remove_var(key); }
#[test]
fn env_lock_is_reentrant_safe_across_calls() {
{
let _g = env_lock();
}
let _g2 = env_lock();
}
}