This method writes to a temporary file then renames in place
Details
Details
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
Alphare |
hg-reviewers |
This method writes to a temporary file then renames in place
No Linters Available |
No Unit Test Coverage |
Path | Packages | |||
---|---|---|---|---|
M | rust/hg-core/src/errors.rs (6 lines) | |||
M | rust/hg-core/src/vfs.rs (25 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
92c904fb50b5 | 0d80161fd922 | Simon Sapin | Nov 29 2021, 12:46 PM |
Status | Author | Revision | |
---|---|---|---|
Closed | SimonSapin | ||
Closed | SimonSapin | ||
Closed | SimonSapin | ||
Closed | SimonSapin | ||
Closed | SimonSapin | D11836 rust: Add Vfs::write_atomic | |
Closed | SimonSapin | ||
Closed | SimonSapin |
/// given path. | /// given path. | ||||
/// | /// | ||||
/// This allows printing something like “File not found when reading | /// This allows printing something like “File not found when reading | ||||
/// example.txt” instead of just “File not found”. | /// example.txt” instead of just “File not found”. | ||||
/// | /// | ||||
/// Converts a `Result` with `std::io::Error` into one with `HgError`. | /// 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_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( | fn with_context( | ||||
self, | self, | ||||
context: impl FnOnce() -> IoErrorContext, | context: impl FnOnce() -> IoErrorContext, | ||||
) -> Result<T, HgError>; | ) -> Result<T, HgError>; | ||||
} | } | ||||
impl<T> IoResultExt<T> for std::io::Result<T> { | impl<T> IoResultExt<T> for std::io::Result<T> { | ||||
fn when_reading_file(self, path: &std::path::Path) -> Result<T, HgError> { | fn when_reading_file(self, path: &std::path::Path) -> Result<T, HgError> { | ||||
self.with_context(|| IoErrorContext::ReadingFile(path.to_owned())) | 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( | fn with_context( | ||||
self, | self, | ||||
context: impl FnOnce() -> IoErrorContext, | context: impl FnOnce() -> IoErrorContext, | ||||
) -> Result<T, HgError> { | ) -> Result<T, HgError> { | ||||
self.map_err(|error| HgError::IoError { | self.map_err(|error| HgError::IoError { | ||||
error, | error, | ||||
context: context(), | context: context(), | ||||
}) | }) |
use crate::errors::{HgError, IoErrorContext, IoResultExt}; | use crate::errors::{HgError, IoErrorContext, IoResultExt}; | ||||
use memmap2::{Mmap, MmapOptions}; | use memmap2::{Mmap, MmapOptions}; | ||||
use std::io::ErrorKind; | use std::io::{ErrorKind, Write}; | ||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
/// Filesystem access abstraction for the contents of a given "base" diretory | /// Filesystem access abstraction for the contents of a given "base" diretory | ||||
#[derive(Clone, Copy)] | #[derive(Clone, Copy)] | ||||
pub struct Vfs<'a> { | pub struct Vfs<'a> { | ||||
pub(crate) base: &'a Path, | pub(crate) base: &'a Path, | ||||
} | } | ||||
#[cfg(unix)] | #[cfg(unix)] | ||||
pub fn create_symlink( | pub fn create_symlink( | ||||
&self, | &self, | ||||
relative_link_path: impl AsRef<Path>, | relative_link_path: impl AsRef<Path>, | ||||
target_path: impl AsRef<Path>, | target_path: impl AsRef<Path>, | ||||
) -> Result<(), HgError> { | ) -> Result<(), HgError> { | ||||
let link_path = self.join(relative_link_path); | let link_path = self.join(relative_link_path); | ||||
std::os::unix::fs::symlink(target_path, &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(()) | |||||
} | } | ||||
} | } | ||||
fn fs_metadata( | fn fs_metadata( | ||||
path: impl AsRef<Path>, | path: impl AsRef<Path>, | ||||
) -> Result<Option<std::fs::Metadata>, HgError> { | ) -> Result<Option<std::fs::Metadata>, HgError> { | ||||
let path = path.as_ref(); | let path = path.as_ref(); | ||||
match std::fs::metadata(path) { | match std::fs::metadata(path) { |