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) { | ||||