use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug)]
pub struct TempDir(PathBuf);
impl TempDir {
#[must_use]
pub fn new(tag: &str) -> Self {
static NEXT: AtomicU64 = AtomicU64::new(0);
let dir = std::env::temp_dir().join(format!(
"cratefield-{tag}-{}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("the clock is after the epoch")
.as_nanos(),
NEXT.fetch_add(1, Ordering::Relaxed),
));
std::fs::create_dir_all(&dir).expect("a scratch directory");
Self(dir)
}
#[must_use]
pub fn path(&self) -> &Path {
&self.0
}
#[must_use]
pub fn join(&self, name: &str) -> PathBuf {
self.0.join(name)
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_directory_goes_away_even_when_the_test_holding_it_panics() {
let dir = TempDir::new("panics");
let path = dir.path().to_path_buf();
std::fs::write(dir.join("vapid.key"), "a private key").expect("writes");
assert!(path.exists(), "the directory was created");
let result = std::panic::catch_unwind(move || {
let _held = dir;
panic!("an assertion fires here");
});
assert!(result.is_err(), "the panic is the scenario");
assert!(
!path.exists(),
"the unwind left {} behind, private key and all",
path.display()
);
}
#[test]
fn two_directories_taken_in_the_same_tick_are_not_the_same_directory() {
let first = TempDir::new("same-tick");
let second = TempDir::new("same-tick");
assert_ne!(first.path(), second.path());
}
}