#[cfg(test)]
#[path = "env_tests.rs"]
mod env_tests;
use std::env;
use std::ffi::OsString;
use std::sync::{Mutex, MutexGuard, PoisonError, TryLockError};
static ENV_LOCK: Mutex<()> = Mutex::new(());
#[must_use = "dropping the guard immediately releases the environment lock"]
pub fn env_lock() -> MutexGuard<'static, ()> {
ENV_LOCK.lock().unwrap_or_else(PoisonError::into_inner)
}
#[must_use = "dropping the probe immediately releases the environment lock"]
pub fn env_try_lock() -> Option<MutexGuard<'static, ()>> {
match ENV_LOCK.try_lock() {
Ok(guard) => Some(guard),
Err(TryLockError::Poisoned(e)) => Some(e.into_inner()),
Err(TryLockError::WouldBlock) => None,
}
}
#[must_use = "the guard restores environment variables only while it is alive"]
pub struct EnvGuard {
undo: Vec<(String, Option<OsString>)>,
_lock: MutexGuard<'static, ()>,
}
impl EnvGuard {
pub fn acquire() -> Self {
Self {
undo: Vec::new(),
_lock: env_lock(),
}
}
pub fn set(key: impl Into<String>, value: impl Into<OsString>) -> Self {
let mut guard = Self::acquire();
guard.and_set(key, value);
guard
}
pub fn remove(key: impl Into<String>) -> Self {
let mut guard = Self::acquire();
guard.and_remove(key);
guard
}
pub fn and_set(&mut self, key: impl Into<String>, value: impl Into<OsString>) -> &mut Self {
let key = key.into();
let value = value.into();
self.undo.push((key.clone(), env::var_os(&key)));
unsafe { env::set_var(&key, &value) };
self
}
pub fn and_remove(&mut self, key: impl Into<String>) -> &mut Self {
let key = key.into();
self.undo.push((key.clone(), env::var_os(&key)));
unsafe { env::remove_var(&key) };
self
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
while let Some((key, previous)) = self.undo.pop() {
match previous {
Some(previous) => {
unsafe { env::set_var(&key, previous) };
}
None => {
unsafe { env::remove_var(&key) };
}
}
}
}
}
#[cfg(test)]
struct Fixture {
key: &'static str,
ambient: Option<OsString>,
}
#[cfg(test)]
impl Fixture {
fn install(key: &'static str, value: Option<&str>) -> Self {
let _lock = env_lock();
let ambient = env::var_os(key);
match value {
Some(value) => unsafe { env::set_var(key, value) },
None => unsafe { env::remove_var(key) },
}
Self { key, ambient }
}
}
#[cfg(test)]
impl Drop for Fixture {
fn drop(&mut self) {
let _lock = env_lock();
match &self.ambient {
Some(value) => unsafe { env::set_var(self.key, value) },
None => unsafe { env::remove_var(self.key) },
}
}
}