use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::{fmt, io};
enum ScratchDirectoryType {
Path(PathBuf),
TempDir(tempfile::TempDir),
}
#[derive(Clone)]
pub struct ScratchDirectory {
inner: Arc<Inner>,
}
impl fmt::Debug for ScratchDirectory {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ScratchDirectory")
.field("dir", &self.path())
.finish()
}
}
struct Inner {
_parent: Option<Arc<Inner>>,
dir: ScratchDirectoryType,
}
impl ScratchDirectory {
pub fn new_in_dir(dir_path: PathBuf) -> ScratchDirectory {
let inner = Inner {
_parent: None,
dir: ScratchDirectoryType::Path(dir_path),
};
ScratchDirectory {
inner: Arc::new(inner),
}
}
pub fn for_test() -> io::Result<ScratchDirectory> {
let tempdir = tempfile::tempdir()?;
let inner = Inner {
_parent: None,
dir: ScratchDirectoryType::TempDir(tempdir),
};
Ok(ScratchDirectory {
inner: Arc::new(inner),
})
}
pub fn path(&self) -> &Path {
match &self.inner.dir {
ScratchDirectoryType::Path(path) => path,
ScratchDirectoryType::TempDir(tempdir) => tempdir.path(),
}
}
pub fn named_temp_child<S: AsRef<OsStr>>(&self, prefix: S) -> io::Result<Self> {
let temp_dir = tempfile::Builder::new()
.prefix(prefix.as_ref())
.tempdir_in(self.path())?;
let inner = Inner {
_parent: Some(self.inner.clone()),
dir: ScratchDirectoryType::TempDir(temp_dir),
};
Ok(ScratchDirectory {
inner: Arc::new(inner),
})
}
}
#[cfg(test)]
mod tests {
use std::mem;
use super::*;
#[test]
fn test_scratch_directory() -> io::Result<()> {
let parent = ScratchDirectory::for_test()?;
let parent_path = parent.path().to_path_buf();
let child = parent.named_temp_child("child-")?;
let child_path = child.path().to_path_buf();
assert!(child_path
.file_name()
.and_then(|file_name| file_name.to_str())
.map(|file_name| file_name.contains("child-"))
.unwrap_or(false));
mem::drop(parent);
assert!(parent_path.exists());
assert!(child_path.exists());
mem::drop(child);
assert!(!parent_path.exists());
assert!(!child_path.exists());
Ok(())
}
#[test]
fn test_scratch_directory_remove_content() -> io::Result<()> {
let parent = ScratchDirectory::for_test()?;
let parent_path = parent.path().to_path_buf();
std::fs::write(parent.path().join("hello.txt"), b"hello")?;
assert!(parent_path.exists());
mem::drop(parent);
assert!(!parent_path.exists());
Ok(())
}
#[test]
fn test_scratch_directory_in_path() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
let tempdir_path = tempdir.path().to_path_buf();
assert!(tempdir_path.exists());
let parent = ScratchDirectory::new_in_dir(tempdir_path.clone());
assert_eq!(parent.path(), tempdir.path());
assert!(tempdir.path().exists());
let child = parent.named_temp_child("child-")?;
let child_path = child.path().to_path_buf();
assert!(child_path.exists());
mem::drop(child);
assert!(!child_path.exists());
mem::drop(parent);
assert!(!child_path.exists());
mem::drop(tempdir);
assert!(!tempdir_path.exists());
Ok(())
}
}