use std::{
ffi::{OsStr, OsString},
sync::{Mutex, MutexGuard, OnceLock},
};
static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
pub(crate) struct EnvGuard {
_guard: MutexGuard<'static, ()>,
}
impl EnvGuard {
pub(crate) fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(&self, key: K, value: V) {
unsafe { std::env::set_var(key, value) };
}
pub(crate) fn remove_var<K: AsRef<OsStr>>(&self, key: K) {
unsafe { std::env::remove_var(key) };
}
pub(crate) fn save(&self, name: &'static str) -> SavedEnvVar<'_> {
SavedEnvVar {
name,
value: std::env::var_os(name),
guard: self,
}
}
}
pub(crate) fn env_lock() -> EnvGuard {
EnvGuard {
_guard: ENV_LOCK.get_or_init(|| Mutex::new(())).lock().unwrap(),
}
}
pub(crate) struct SavedEnvVar<'a> {
name: &'static str,
value: Option<OsString>,
guard: &'a EnvGuard,
}
impl Drop for SavedEnvVar<'_> {
fn drop(&mut self) {
match self.value.as_ref() {
Some(value) => self.guard.set_var(self.name, value),
None => self.guard.remove_var(self.name),
}
}
}
static CURRENT_DIR_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
pub(crate) struct CurrentDirGuard {
original: std::path::PathBuf,
_guard: MutexGuard<'static, ()>,
restored: bool,
}
impl CurrentDirGuard {
pub(crate) fn capture() -> Self {
let guard = CURRENT_DIR_LOCK
.get_or_init(|| Mutex::new(()))
.lock()
.unwrap();
Self {
original: std::env::current_dir().unwrap(),
_guard: guard,
restored: false,
}
}
pub(crate) fn set_current_dir(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
std::env::set_current_dir(path)
}
pub(crate) fn restore(&mut self) -> std::io::Result<()> {
let result = std::env::set_current_dir(&self.original);
if result.is_ok() {
self.restored = true;
}
result
}
}
impl Drop for CurrentDirGuard {
fn drop(&mut self) {
if !self.restored {
let _ = std::env::set_current_dir(&self.original);
}
}
}