diff options
Diffstat (limited to 'src/fileutil.rs')
-rw-r--r-- | src/fileutil.rs | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/fileutil.rs b/src/fileutil.rs new file mode 100644 index 0000000..0bbafc0 --- /dev/null +++ b/src/fileutil.rs @@ -0,0 +1,97 @@ +use std::ffi::OsString; +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; +use std::path::{Path, PathBuf}; +use std::{fs, io, mem}; + +#[repr(transparent)] +pub struct Temp { + path: PathBuf, +} + +impl Drop for Temp { + fn drop(&mut self) { + if self.path.as_os_str().is_empty() { + return; + } + fs::remove_file(&self.path).expect("Failed to clean up temporary file"); + } +} + +impl Temp { + #[inline] + pub fn path(&self) -> &Path { + &*self.path + } + + #[inline] + pub fn leave(mut self) -> PathBuf { + mem::replace(&mut self.path, PathBuf::new()) + } + + #[inline] + pub fn rename_to(self, to: impl AsRef<Path>) -> io::Result<()> { + fs::rename(self.leave(), to) + } +} + +pub struct Writer { + inner: Temp, + file: fs::File, +} + +impl Writer { + pub fn new(path: PathBuf) -> io::Result<Self> { + let mut file = fs::OpenOptions::new(); + file.create_new(true); + file.append(true); + #[cfg(unix)] + file.mode(0o0600); + let file = file.open(&path)?; + + Ok(Self { + inner: Temp { path }, + file, + }) + } + + #[inline] + pub fn file(&mut self) -> &mut fs::File { + &mut self.file + } + + #[inline] + pub fn sync_done(self) -> io::Result<Temp> { + self.file.sync_data()?; + Ok(self.inner) + } + + #[inline] + pub fn done(self) -> Temp { + self.inner + } +} + +pub fn update(path: &Path, data: &[u8]) -> io::Result<()> { + let mut tmp = OsString::from(path); + tmp.push(".tmp"); + let mut tmp = Writer::new(PathBuf::from(tmp))?; + io::Write::write_all(tmp.file(), data)?; + tmp.sync_done()?.rename_to(path) +} + +pub fn load(path: &Path) -> io::Result<Option<Vec<u8>>> { + let mut file = match fs::File::open(&path) { + Ok(file) => file, + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + return Ok(None); + } + return Err(e); + } + }; + + let mut data = Vec::new(); + io::Read::read_to_end(&mut file, &mut data)?; + Ok(Some(data)) +} |