Show More
@@ -151,6 +151,8 b' pub trait IoResultExt<T> {' | |||||
151 | /// Converts a `Result` with `std::io::Error` into one with `HgError`. |
|
151 | /// Converts a `Result` with `std::io::Error` into one with `HgError`. | |
152 | fn when_reading_file(self, path: &std::path::Path) -> Result<T, HgError>; |
|
152 | fn when_reading_file(self, path: &std::path::Path) -> Result<T, HgError>; | |
153 |
|
153 | |||
|
154 | fn when_writing_file(self, path: &std::path::Path) -> Result<T, HgError>; | |||
|
155 | ||||
154 | fn with_context( |
|
156 | fn with_context( | |
155 | self, |
|
157 | self, | |
156 | context: impl FnOnce() -> IoErrorContext, |
|
158 | context: impl FnOnce() -> IoErrorContext, | |
@@ -162,6 +164,10 b' impl<T> IoResultExt<T> for std::io::Resu' | |||||
162 | self.with_context(|| IoErrorContext::ReadingFile(path.to_owned())) |
|
164 | self.with_context(|| IoErrorContext::ReadingFile(path.to_owned())) | |
163 | } |
|
165 | } | |
164 |
|
166 | |||
|
167 | fn when_writing_file(self, path: &std::path::Path) -> Result<T, HgError> { | |||
|
168 | self.with_context(|| IoErrorContext::WritingFile(path.to_owned())) | |||
|
169 | } | |||
|
170 | ||||
165 | fn with_context( |
|
171 | fn with_context( | |
166 | self, |
|
172 | self, | |
167 | context: impl FnOnce() -> IoErrorContext, |
|
173 | context: impl FnOnce() -> IoErrorContext, |
@@ -1,6 +1,6 b'' | |||||
1 | use crate::errors::{HgError, IoErrorContext, IoResultExt}; |
|
1 | use crate::errors::{HgError, IoErrorContext, IoResultExt}; | |
2 | use memmap2::{Mmap, MmapOptions}; |
|
2 | use memmap2::{Mmap, MmapOptions}; | |
3 | use std::io::ErrorKind; |
|
3 | use std::io::{ErrorKind, Write}; | |
4 | use std::path::{Path, PathBuf}; |
|
4 | use std::path::{Path, PathBuf}; | |
5 |
|
5 | |||
6 | /// Filesystem access abstraction for the contents of a given "base" diretory |
|
6 | /// Filesystem access abstraction for the contents of a given "base" diretory | |
@@ -105,7 +105,28 b" impl Vfs<'_> {" | |||||
105 | ) -> Result<(), HgError> { |
|
105 | ) -> Result<(), HgError> { | |
106 | let link_path = self.join(relative_link_path); |
|
106 | let link_path = self.join(relative_link_path); | |
107 | std::os::unix::fs::symlink(target_path, &link_path) |
|
107 | std::os::unix::fs::symlink(target_path, &link_path) | |
108 |
.w |
|
108 | .when_writing_file(&link_path) | |
|
109 | } | |||
|
110 | ||||
|
111 | /// Write `contents` into a temporary file, then rename to `relative_path`. | |||
|
112 | /// This makes writing to a file "atomic": a reader opening that path will | |||
|
113 | /// see either the previous contents of the file or the complete new | |||
|
114 | /// content, never a partial write. | |||
|
115 | pub fn atomic_write( | |||
|
116 | &self, | |||
|
117 | relative_path: impl AsRef<Path>, | |||
|
118 | contents: &[u8], | |||
|
119 | ) -> Result<(), HgError> { | |||
|
120 | let mut tmp = tempfile::NamedTempFile::new_in(self.base) | |||
|
121 | .when_writing_file(self.base)?; | |||
|
122 | tmp.write_all(contents) | |||
|
123 | .and_then(|()| tmp.flush()) | |||
|
124 | .when_writing_file(tmp.path())?; | |||
|
125 | let path = self.join(relative_path); | |||
|
126 | tmp.persist(&path) | |||
|
127 | .map_err(|e| e.error) | |||
|
128 | .when_writing_file(&path)?; | |||
|
129 | Ok(()) | |||
109 | } |
|
130 | } | |
110 | } |
|
131 | } | |
111 |
|
132 |
General Comments 0
You need to be logged in to leave comments.
Login now