use crate::{AutoRemove, ContainingDirectory, ForksafeTempfile, Registration, NEXT_MAP_INDEX, REGISTER};
use std::{io, path::Path};
use tempfile::NamedTempFile;
impl Registration {
pub fn at_path(
path: impl AsRef<Path>,
directory: ContainingDirectory,
cleanup: AutoRemove,
) -> io::Result<Registration> {
let path = path.as_ref();
let tempfile = {
let mut builder = tempfile::Builder::new();
let dot_ext_storage;
match path.file_stem() {
Some(stem) => builder.prefix(stem),
None => builder.prefix(""),
};
if let Some(ext) = path.extension() {
dot_ext_storage = format!(".{}", ext.to_string_lossy());
builder.suffix(&dot_ext_storage);
}
let parent_dir = path.parent().expect("parent directory is present");
let parent_dir = directory.resolve(parent_dir)?;
ForksafeTempfile::new(builder.rand_bytes(0).tempfile_in(parent_dir)?, cleanup)
};
let id = NEXT_MAP_INDEX.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
expect_none(REGISTER.insert(id, Some(tempfile)));
Ok(Registration { id })
}
pub fn new(
containing_directory: impl AsRef<Path>,
directory: ContainingDirectory,
cleanup: AutoRemove,
) -> io::Result<Registration> {
let id = NEXT_MAP_INDEX.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let containing_directory = directory.resolve(containing_directory.as_ref())?;
expect_none(REGISTER.insert(
id,
Some(ForksafeTempfile::new(
NamedTempFile::new_in(containing_directory)?,
cleanup,
)),
));
Ok(Registration { id })
}
pub fn take(self) -> Option<NamedTempFile> {
let res = REGISTER.remove(&self.id);
std::mem::forget(self);
res.and_then(|(_k, v)| v.map(|v| v.inner))
}
}
impl ContainingDirectory {
fn resolve(self, dir: &Path) -> std::io::Result<&Path> {
match self {
ContainingDirectory::Exists => Ok(dir),
ContainingDirectory::CreateAllRaceProof(retries) => crate::create_dir::all(dir, retries),
}
}
}
fn expect_none<T>(v: Option<T>) {
assert!(
v.is_none(),
"there should never be conflicts or old values as ids are never reused."
);
}
impl Drop for Registration {
fn drop(&mut self) {
if let Some((_id, Some(tempfile))) = REGISTER.remove(&self.id) {
let directory = tempfile
.inner
.path()
.parent()
.expect("every tempfile has a parent directory")
.to_owned();
drop(tempfile.inner);
tempfile.cleanup.execute_best_effort(&directory);
}
}
}