use std::env::{remove_var, set_var, var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::path::PathBuf;
use std::sync::{Mutex, MutexGuard};
use tempfile::TempDir;
static ENV_MUTEX: Mutex<()> = Mutex::new(());
pub struct TempHome {
_guard: MutexGuard<'static, ()>,
dir: TempDir,
old_home: Option<String>,
overrides: Vec<(String, Option<OsString>)>,
}
impl TempHome {
pub fn new() -> Self {
let guard = ENV_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
let temp_dir = TempDir::new().expect("Should create temp dir");
let old_home = var("HOME").ok();
set_var("HOME", temp_dir.path());
Self {
_guard: guard,
dir: temp_dir,
old_home,
overrides: Vec::new(),
}
}
pub fn absolute(
&self,
name: &str,
) -> String {
self.path().join(name).to_string_lossy().to_string()
}
pub fn path(&self) -> PathBuf {
self.dir.path().to_path_buf()
}
pub fn set_env(
&mut self,
key: &str,
value: impl AsRef<OsStr>,
) {
self.remember(key);
set_var(key, value);
}
pub fn touch(
&self,
name: &str,
) {
fs::File::create(self.path().join(name)).expect("Should create file");
}
pub fn unset_env(
&mut self,
key: &str,
) {
self.remember(key);
remove_var(key);
}
fn remember(
&mut self,
key: &str,
) {
if !self.overrides.iter().any(|(name, _)| name == key) {
self.overrides.push((key.to_string(), var_os(key)));
}
}
}
impl Default for TempHome {
fn default() -> Self {
Self::new()
}
}
impl Drop for TempHome {
fn drop(&mut self) {
for (key, value) in self.overrides.drain(..) {
match value {
Some(value) => set_var(&key, value),
None => remove_var(&key),
}
}
match &self.old_home {
Some(home) => set_var("HOME", home),
None => remove_var("HOME"),
}
}
}