use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
pub(crate) fn unique_stamp() -> String {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
format!("{}-{nanos}-{seq}", std::process::id())
}
pub(crate) struct Scratch(Vec<PathBuf>);
impl Scratch {
pub(crate) fn new(path: PathBuf) -> Self {
Scratch(vec![path])
}
pub(crate) fn all(paths: Vec<PathBuf>) -> Self {
Scratch(paths)
}
}
impl Drop for Scratch {
fn drop(&mut self) {
for path in &self.0 {
let _ = std::fs::remove_file(path);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn stamps_are_unique_across_calls() {
assert_ne!(unique_stamp(), unique_stamp());
}
#[test]
fn drop_removes_a_single_path() {
let dir = Path::new("target").join(format!("scratch-one-{}", unique_stamp()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("temp.part");
std::fs::write(&path, b"x").unwrap();
{
let _guard = Scratch::new(path.clone());
assert!(path.exists());
}
assert!(!path.exists());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn drop_removes_all_paths() {
let dir = Path::new("target").join(format!("scratch-many-{}", unique_stamp()));
std::fs::create_dir_all(&dir).unwrap();
let a = dir.join("a.part");
let b = dir.join("b.part");
std::fs::write(&a, b"a").unwrap();
std::fs::write(&b, b"b").unwrap();
drop(Scratch::all(vec![a.clone(), b.clone()]));
assert!(!a.exists());
assert!(!b.exists());
let _ = std::fs::remove_dir_all(&dir);
}
}