use std::ffi::OsString;
use std::panic;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
pub(crate) static ENV_LOCK: Mutex<()> = Mutex::new(());
pub(crate) struct EnvGuard {
key: &'static str,
old: Option<OsString>,
}
impl EnvGuard {
pub(crate) fn set(key: &'static str, val: &Path) -> Self {
let old = std::env::var_os(key);
unsafe { std::env::set_var(key, val) };
Self { key, old }
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
match &self.old {
Some(v) => unsafe { std::env::set_var(self.key, v) },
None => unsafe { std::env::remove_var(self.key) },
}
}
}
pub(crate) struct CwdGuard {
old: PathBuf,
}
impl CwdGuard {
pub(crate) fn set(new: &Path) -> Self {
let old = std::env::current_dir().expect("current_dir");
std::env::set_current_dir(new).expect("set_current_dir");
Self { old }
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.old);
}
}
pub(crate) fn with_fake_env<F, R>(set_cwd: bool, f: F) -> R
where
F: panic::UnwindSafe + FnOnce(&Path, Option<&Path>) -> R,
{
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let home_tmp = tempfile::tempdir().expect("home tempdir");
let _home_guard = EnvGuard::set("HOME", home_tmp.path());
let _userprofile_guard = EnvGuard::set("USERPROFILE", home_tmp.path());
let cwd_tmp = if set_cwd {
Some(tempfile::tempdir().expect("cwd tempdir"))
} else {
None
};
let _cwd_guard = cwd_tmp.as_ref().map(|t| CwdGuard::set(t.path()));
let cwd_path = cwd_tmp.as_ref().map(|t| t.path().to_path_buf());
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
f(home_tmp.path(), cwd_path.as_deref())
}));
match result {
Ok(v) => v,
Err(e) => panic::resume_unwind(e),
}
}