use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
pub struct Scratch {
path: PathBuf,
}
impl Deref for Scratch {
type Target = Path;
fn deref(&self) -> &Path {
&self.path
}
}
impl AsRef<Path> for Scratch {
fn as_ref(&self) -> &Path {
&self.path
}
}
impl AsRef<std::ffi::OsStr> for Scratch {
fn as_ref(&self) -> &std::ffi::OsStr {
self.path.as_os_str()
}
}
impl Drop for Scratch {
fn drop(&mut self) {
if std::thread::panicking() {
eprintln!("scratch kept for inspection: {}", self.path.display());
return;
}
let _ = std::fs::remove_dir_all(&self.path);
}
}
pub fn case(name: &str) -> Scratch {
static NEXT: AtomicU64 = AtomicU64::new(0);
let serial = NEXT.fetch_add(1, Ordering::Relaxed);
let path = root().join(format!("{name}-{}-{serial}", std::process::id()));
let _ = std::fs::remove_dir_all(&path);
std::fs::create_dir_all(&path)
.unwrap_or_else(|err| panic!("cannot create {}: {err}", path.display()));
Scratch { path }
}
fn root() -> PathBuf {
let path = match option_env!("CARGO_TARGET_TMPDIR") {
Some(path) => PathBuf::from(path),
None => target_dir().join("tmp"),
};
std::fs::create_dir_all(&path)
.unwrap_or_else(|err| panic!("cannot create {}: {err}", path.display()));
path
}
fn target_dir() -> PathBuf {
if let Some(dir) = std::env::var_os("CARGO_TARGET_DIR") {
return PathBuf::from(dir);
}
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
manifest
.parent()
.and_then(Path::parent)
.unwrap_or(manifest)
.join("target")
}