use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0);
pub fn temp_dir() -> TempDir {
let base = std::env::temp_dir().join("rvtest");
let counter = TEMP_COUNTER.fetch_add(1, Ordering::SeqCst);
let path = base.join(&format!("{:06x}", counter));
ensure_empty(&path);
TempDir { path }
}
pub fn temp_dir_with_prefix(prefix: &str) -> TempDir {
let base = std::env::temp_dir().join("rvtest");
let counter = TEMP_COUNTER.fetch_add(1, Ordering::SeqCst);
let path = base.join(&format!("{}-{:06x}", prefix, counter));
ensure_empty(&path);
TempDir { path }
}
fn ensure_empty(path: &Path) {
let _ = std::fs::remove_dir_all(path);
std::fs::create_dir_all(path).expect("failed to create temp directory");
}
pub struct TempDir {
path: PathBuf,
}
impl TempDir {
pub fn path(&self) -> &Path {
&self.path
}
pub fn leak(self) -> PathBuf {
let path = self.path.clone();
std::mem::forget(self);
path
}
}
impl AsRef<Path> for TempDir {
fn as_ref(&self) -> &Path {
&self.path
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}
#[cfg(test)]
mod tests {
use super::{temp_dir, temp_dir_with_prefix};
use std::path::Path;
#[test]
fn creates_directory() {
let dir = temp_dir();
assert!(dir.path().exists());
assert!(dir.path().is_dir());
}
#[test]
fn is_writable() {
let dir = temp_dir();
let file = dir.path().join("test.txt");
std::fs::write(&file, b"hello").unwrap();
assert!(file.exists());
}
#[test]
fn cleaned_on_drop() {
let path;
{
let dir = temp_dir();
path = dir.path().to_owned();
assert!(path.exists());
}
assert!(!path.exists(), "directory should be deleted after drop");
}
#[test]
fn with_prefix_contains_prefix() {
let dir = temp_dir_with_prefix("myapp");
assert!(dir.path().exists());
assert!(dir.path().to_string_lossy().contains("myapp"));
}
#[test]
fn cleaned_after_panic() {
let path;
{
let dir = temp_dir();
path = dir.path().to_owned();
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
panic!("intentional");
}));
assert!(r.is_err());
}
assert!(!path.exists(), "directory should be deleted after panic + drop");
}
#[test]
fn as_ref_path() {
let dir = temp_dir();
let p: &Path = dir.as_ref();
assert!(p.exists());
}
#[test]
fn leak_keeps_directory() {
let path;
{
let dir = temp_dir();
path = dir.leak();
assert!(path.exists());
}
assert!(path.exists());
let _ = std::fs::remove_dir_all(&path);
}
#[test]
fn multiple_dirs_have_unique_paths() {
let a = temp_dir();
let b = temp_dir();
assert_ne!(a.path(), b.path());
}
#[test]
fn with_prefix_unique() {
let a = temp_dir_with_prefix("test");
let b = temp_dir_with_prefix("test");
assert_ne!(a.path(), b.path());
}
}