use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
pub fn env_lock() -> MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(PoisonError::into_inner)
}
pub struct PinnedRecursiveHome {
_guard: MutexGuard<'static, ()>,
prev: Option<std::ffi::OsString>,
}
impl PinnedRecursiveHome {
pub fn new(path: impl AsRef<std::path::Path>) -> Self {
let guard = env_lock();
let prev = std::env::var_os("RECURSIVE_HOME");
unsafe {
std::env::set_var("RECURSIVE_HOME", path.as_ref().as_os_str());
}
Self {
_guard: guard,
prev,
}
}
}
impl Drop for PinnedRecursiveHome {
fn drop(&mut self) {
unsafe {
match self.prev.take() {
Some(v) => std::env::set_var("RECURSIVE_HOME", v),
None => std::env::remove_var("RECURSIVE_HOME"),
}
}
}
}
pub struct PinnedHome {
_guard: MutexGuard<'static, ()>,
prev_home: Option<std::ffi::OsString>,
prev_userprofile: Option<std::ffi::OsString>,
}
impl PinnedHome {
pub fn new(path: impl AsRef<std::path::Path>) -> Self {
let guard = env_lock();
let prev_home = std::env::var_os("HOME");
unsafe {
std::env::set_var("HOME", path.as_ref().as_os_str());
}
let prev_userprofile = if cfg!(windows) {
let prev = std::env::var_os("USERPROFILE");
unsafe {
std::env::set_var("USERPROFILE", path.as_ref().as_os_str());
}
prev
} else {
None
};
Self {
_guard: guard,
prev_home,
prev_userprofile,
}
}
}
impl Drop for PinnedHome {
fn drop(&mut self) {
unsafe {
match self.prev_home.take() {
Some(v) => std::env::set_var("HOME", v),
None => std::env::remove_var("HOME"),
}
if cfg!(windows) {
match self.prev_userprofile.take() {
Some(v) => std::env::set_var("USERPROFILE", v),
None => std::env::remove_var("USERPROFILE"),
}
}
}
}
}
pub struct IsolatedWorkspace {
workspace: tempfile::TempDir,
_home: tempfile::TempDir,
_pin: PinnedRecursiveHome,
}
impl IsolatedWorkspace {
pub fn new() -> Self {
let home = tempfile::tempdir().expect("home tempdir");
let pin = PinnedRecursiveHome::new(home.path());
let workspace = tempfile::tempdir().expect("workspace tempdir");
Self {
workspace,
_home: home,
_pin: pin,
}
}
pub fn path(&self) -> &std::path::Path {
self.workspace.path()
}
}
impl Default for IsolatedWorkspace {
fn default() -> Self {
Self::new()
}
}