use std::fs;
use std::path::PathBuf;
#[derive(Clone, Copy)]
enum Kind {
File,
Dir,
}
pub(crate) struct PathGuard {
path: Option<PathBuf>,
kind: Kind,
}
impl PathGuard {
pub(crate) fn file(path: PathBuf) -> Self {
Self {
path: Some(path),
kind: Kind::File,
}
}
pub(crate) fn dir(path: PathBuf) -> Self {
Self {
path: Some(path),
kind: Kind::Dir,
}
}
pub(crate) fn commit(mut self) -> PathBuf {
self.path
.take()
.expect("PathGuard::commit called after path released")
}
}
impl Drop for PathGuard {
fn drop(&mut self) {
if let Some(p) = self.path.take() {
match self.kind {
Kind::File => drop(fs::remove_file(&p)),
Kind::Dir => drop(fs::remove_dir_all(&p)),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn file_drop_removes() {
let tmp = std::env::temp_dir().join(format!(
"pbfhogg-path-guard-test-{}.tmp",
std::process::id()
));
fs::write(&tmp, b"hello").expect("write test file");
{
let _g = PathGuard::file(tmp.clone());
}
assert!(!tmp.exists(), "file should be removed on drop");
}
#[test]
fn file_commit_preserves() {
let tmp = std::env::temp_dir().join(format!(
"pbfhogg-path-guard-commit-{}.tmp",
std::process::id()
));
fs::write(&tmp, b"hello").expect("write test file");
{
let g = PathGuard::file(tmp.clone());
let returned = g.commit();
assert_eq!(returned, tmp);
}
assert!(tmp.exists(), "file should survive after commit");
fs::remove_file(&tmp).expect("cleanup test file");
}
#[test]
fn dir_drop_removes_recursively() {
let tmp = std::env::temp_dir().join(format!(
"pbfhogg-path-guard-dir-{}",
std::process::id()
));
fs::create_dir_all(&tmp).expect("create test dir");
fs::write(tmp.join("inside.txt"), b"x").expect("write test file");
{
let _g = PathGuard::dir(tmp.clone());
}
assert!(!tmp.exists(), "dir should be removed recursively on drop");
}
}