Mercurial > hg
changeset 48418:abeae090ce67
rust: Add Vfs::write_atomic
This method writes to a temporary file then renames in place
Differential Revision: https://phab.mercurial-scm.org/D11836
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Mon, 29 Nov 2021 18:46:04 +0100 |
parents | 5734b03ecf3e |
children | c8659e61073d |
files | rust/hg-core/src/errors.rs rust/hg-core/src/vfs.rs |
diffstat | 2 files changed, 29 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/hg-core/src/errors.rs Mon Mar 22 09:07:10 2021 +0100 +++ b/rust/hg-core/src/errors.rs Mon Nov 29 18:46:04 2021 +0100 @@ -151,6 +151,8 @@ /// Converts a `Result` with `std::io::Error` into one with `HgError`. fn when_reading_file(self, path: &std::path::Path) -> Result<T, HgError>; + fn when_writing_file(self, path: &std::path::Path) -> Result<T, HgError>; + fn with_context( self, context: impl FnOnce() -> IoErrorContext, @@ -162,6 +164,10 @@ self.with_context(|| IoErrorContext::ReadingFile(path.to_owned())) } + fn when_writing_file(self, path: &std::path::Path) -> Result<T, HgError> { + self.with_context(|| IoErrorContext::WritingFile(path.to_owned())) + } + fn with_context( self, context: impl FnOnce() -> IoErrorContext,
--- a/rust/hg-core/src/vfs.rs Mon Mar 22 09:07:10 2021 +0100 +++ b/rust/hg-core/src/vfs.rs Mon Nov 29 18:46:04 2021 +0100 @@ -1,6 +1,6 @@ use crate::errors::{HgError, IoErrorContext, IoResultExt}; use memmap2::{Mmap, MmapOptions}; -use std::io::ErrorKind; +use std::io::{ErrorKind, Write}; use std::path::{Path, PathBuf}; /// Filesystem access abstraction for the contents of a given "base" diretory @@ -105,7 +105,28 @@ ) -> Result<(), HgError> { let link_path = self.join(relative_link_path); std::os::unix::fs::symlink(target_path, &link_path) - .with_context(|| IoErrorContext::WritingFile(link_path)) + .when_writing_file(&link_path) + } + + /// Write `contents` into a temporary file, then rename to `relative_path`. + /// This makes writing to a file "atomic": a reader opening that path will + /// see either the previous contents of the file or the complete new + /// content, never a partial write. + pub fn atomic_write( + &self, + relative_path: impl AsRef<Path>, + contents: &[u8], + ) -> Result<(), HgError> { + let mut tmp = tempfile::NamedTempFile::new_in(self.base) + .when_writing_file(self.base)?; + tmp.write_all(contents) + .and_then(|()| tmp.flush()) + .when_writing_file(tmp.path())?; + let path = self.join(relative_path); + tmp.persist(&path) + .map_err(|e| e.error) + .when_writing_file(&path)?; + Ok(()) } }