use std::path::{Path, PathBuf};
use std::sync::{Mutex, MutexGuard, PoisonError};
static CWD_LOCK: Mutex<()> = Mutex::new(());
pub struct CwdGuard {
_lock: MutexGuard<'static, ()>,
original_dir: PathBuf,
}
impl CwdGuard {
pub fn enter(dir: &Path) -> Self {
let lock = CWD_LOCK.lock().unwrap_or_else(PoisonError::into_inner);
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(dir).unwrap();
Self {
_lock: lock,
original_dir,
}
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
let restored = std::env::set_current_dir(&self.original_dir);
if !std::thread::panicking() {
restored.expect("CwdGuard failed to restore original working directory");
}
}
}
#[cfg(test)]
mod tests {
use super::CwdGuard;
#[test]
fn test_cwd_guard_restores_cwd_on_panic() {
let original_dir = std::env::current_dir().unwrap();
let tmp_dir = tempfile::TempDir::new().unwrap();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _guard = CwdGuard::enter(tmp_dir.path());
panic!("boom");
}));
assert!(result.is_err());
assert_eq!(std::env::current_dir().unwrap(), original_dir);
}
}