use std::fs::{self, File, OpenOptions};
use std::path::{Path, PathBuf};
pub struct GuardedFile<P: AsRef<Path>> {
file: Option<File>,
path: P,
}
impl<P: AsRef<Path>> GuardedFile<P> {
pub fn for_scope<T, F: FnOnce(&mut File) -> std::io::Result<T>>(path: P, f: F) -> std::io::Result<T> {
let mut gfile = Self::create(path)?;
let result = f(gfile.as_file_mut())?;
gfile.persist();
Ok(result)
}
pub fn create(path: P) -> std::io::Result<Self> {
let dirname = path.as_ref().parent().expect("invalid file name");
fs::create_dir_all(dirname)?;
let file = OpenOptions::new()
.read(true).write(true).create(true).truncate(true)
.open(Self::build_tmp_path(path.as_ref()))?;
Ok(Self { file: Some(file), path })
}
pub fn persist(mut self) -> File {
fs::rename(Self::build_tmp_path(self.path.as_ref()), self.path.as_ref()).expect("failed to persist file");
self.file.take().unwrap()
}
pub fn as_file_mut(&mut self) -> &mut File {
self.file.as_mut().unwrap()
}
fn build_tmp_path(path: &Path) -> PathBuf {
let mut s = path.as_os_str().to_owned();
s.push(".tmp");
s.into()
}
}
impl<P: AsRef<Path>> Drop for GuardedFile<P> {
fn drop(&mut self) {
let _ = fs::remove_file(Self::build_tmp_path(self.path.as_ref())); }
}