# `current_dir` [![Crates.io Version](https://img.shields.io/crates/v/current_dir)](https://crates.io/crates/current_dir) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/G-M0N3Y-2503/current_dir/ci.yml?branch=main)](https://github.com/G-M0N3Y-2503/current_dir/actions?query=branch%3Amain) [![docs.rs](https://img.shields.io/docsrs/current_dir)](https://docs.rs/current_dir/) [![Code Coverage Minimum](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FG-M0N3Y-2503%2Fcurrent_dir%2Fmain%2Ftarpaulin.toml&query=%24.report.fail-under&suffix=%25&label=coverage)](https://github.com/G-M0N3Y-2503/current_dir/blob/main/tarpaulin.toml) A utility crate that helps using [`set_current_dir()`][set_current_dir] and [`current_dir()`][current_dir] in a thread safe manner.
This is generally useful for `#[test]`s that depend on different current working directories each as they are run in multiple threads by default. ### Why can't I just use [`set_current_dir()`][set_current_dir] and [`current_dir()`][current_dir] directly? The current working directory is global to the whole process, so if you only use a single thread or you never change the current working directory, go ahead!
Otherwise, changing the current working directory without synchronising may lead to unexpected behaviour. ## [`Cwd`][Cwd] Example ```rust # use std::error::Error; # fn main() -> Result<(), Box> { use std::env::temp_dir; use current_dir::*; let mut locked_cwd = Cwd::mutex().lock()?; locked_cwd.set(temp_dir())?; // cwd == /tmp # assert_eq!(locked_cwd.get()?, temp_dir()); # drop(locked_cwd); # # Ok(()) # } ``` or you can just use [`set_current_dir()`][set_current_dir] and [`current_dir()`][current_dir] with a locked current working directory. ```rust # use std::error::Error; # fn main() -> Result<(), Box> { use std::env::{set_current_dir, temp_dir}; use current_dir::*; let locked_cwd = Cwd::mutex().lock()?; set_current_dir(temp_dir())?; // cwd == /tmp # assert_eq!(locked_cwd.get()?, temp_dir()); # drop(locked_cwd); # # Ok(()) # } ``` ## [`CwdGuard`][CwdGuard] Example ```rust # use std::error::Error; # fn main() -> Result<(), Box> { use std::{env::temp_dir, fs::create_dir_all}; use current_dir::*; # # let test_dirs = temp_dir().join("sub/sub"); # if !test_dirs.exists() { # create_dir_all(&test_dirs)?; # } let mut locked_cwd = Cwd::mutex().lock()?; locked_cwd.set(temp_dir())?; // cwd == /tmp # assert_eq!(locked_cwd.get()?, temp_dir()); { let mut cwd_guard = CwdGuard::try_from(&mut *locked_cwd)?; cwd_guard.set("sub")?; // cwd == /tmp/sub # assert_eq!(cwd_guard.get()?, temp_dir().join("sub")); { let mut sub_cwd_guard = CwdGuard::try_from(&mut cwd_guard)?; sub_cwd_guard.set("sub")?; // cwd == /tmp/sub/sub # assert_eq!(sub_cwd_guard.get()?, temp_dir().join("sub/sub")); { let mut sub_sub_cwd_guard = CwdGuard::try_from(&mut sub_cwd_guard)?; sub_sub_cwd_guard.set(temp_dir())?; // cwd == /tmp # assert_eq!(sub_sub_cwd_guard.get()?, temp_dir()); } // cwd == /tmp/sub/sub # assert_eq!(sub_cwd_guard.get()?, temp_dir().join("sub/sub")); } // cwd == /tmp/sub # assert_eq!(cwd_guard.get()?, temp_dir().join("sub")); } // cwd == /tmp # assert_eq!(locked_cwd.get()?, temp_dir()); # drop(locked_cwd); # # Ok(()) # } ``` ## Poison cleanup Example ```rust # use std::error::Error; # fn main() -> Result<(), Box> { use std::{ env::temp_dir, error::Error, fs::{create_dir_all, remove_dir}, panic, }; use current_dir::*; let test_dir = temp_dir().join("cwd"); # if !test_dir.exists() { # create_dir_all(&test_dir)?; # } panic::catch_unwind(|| { let mut locked_cwd = Cwd::mutex().lock().unwrap(); locked_cwd.set(&test_dir)?; // removing the CWD before the CwdGuard is dropped will cause a panic on drop. let cwd_guard = CwdGuard::try_from(&mut *locked_cwd)?; remove_dir(&test_dir)?; drop(cwd_guard); # # Ok::<_, Box>(()) }) .expect_err("panicked"); let mut poisoned_locked_cwd = Cwd::mutex().lock().expect_err("cwd poisoned"); let expected_cwd = poisoned_locked_cwd.get_ref().get_expected().unwrap(); # assert_eq!(expected_cwd, test_dir); // Fix poisoned cwd create_dir_all(&expected_cwd)?; poisoned_locked_cwd.get_mut().set(&expected_cwd)?; Cwd::mutex().clear_poison(); let locked_cwd = poisoned_locked_cwd.into_inner(); # drop(locked_cwd); # # Ok(()) # } ``` [Cwd]: https://docs.rs/current_dir/latest/current_dir/struct.Cwd.html [CwdGuard]: https://docs.rs/current_dir/latest/current_dir/struct.CwdGuard.html [CwdStack]: https://docs.rs/current_dir/latest/current_dir/struct.CwdStack.html [set_current_dir]: "std::env::set_current_dir()" [current_dir]: "std::env::current_dir()"