|
1 use crate::errors::{HgError, IoErrorContext, IoResultExt}; |
|
2 use memmap::{Mmap, MmapOptions}; |
|
3 use std::io::ErrorKind; |
|
4 use std::path::{Path, PathBuf}; |
|
5 |
|
6 /// Filesystem access abstraction for the contents of a given "base" diretory |
|
7 #[derive(Clone, Copy)] |
|
8 pub struct Vfs<'a> { |
|
9 pub(crate) base: &'a Path, |
|
10 } |
|
11 |
|
12 impl Vfs<'_> { |
|
13 pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf { |
|
14 self.base.join(relative_path) |
|
15 } |
|
16 |
|
17 pub fn read( |
|
18 &self, |
|
19 relative_path: impl AsRef<Path>, |
|
20 ) -> Result<Vec<u8>, HgError> { |
|
21 let path = self.join(relative_path); |
|
22 std::fs::read(&path).when_reading_file(&path) |
|
23 } |
|
24 |
|
25 pub fn mmap_open( |
|
26 &self, |
|
27 relative_path: impl AsRef<Path>, |
|
28 ) -> Result<Mmap, HgError> { |
|
29 let path = self.base.join(relative_path); |
|
30 let file = std::fs::File::open(&path).when_reading_file(&path)?; |
|
31 // TODO: what are the safety requirements here? |
|
32 let mmap = unsafe { MmapOptions::new().map(&file) } |
|
33 .when_reading_file(&path)?; |
|
34 Ok(mmap) |
|
35 } |
|
36 |
|
37 pub fn rename( |
|
38 &self, |
|
39 relative_from: impl AsRef<Path>, |
|
40 relative_to: impl AsRef<Path>, |
|
41 ) -> Result<(), HgError> { |
|
42 let from = self.join(relative_from); |
|
43 let to = self.join(relative_to); |
|
44 std::fs::rename(&from, &to) |
|
45 .with_context(|| IoErrorContext::RenamingFile { from, to }) |
|
46 } |
|
47 } |
|
48 |
|
49 fn fs_metadata( |
|
50 path: impl AsRef<Path>, |
|
51 ) -> Result<Option<std::fs::Metadata>, HgError> { |
|
52 let path = path.as_ref(); |
|
53 match std::fs::metadata(path) { |
|
54 Ok(meta) => Ok(Some(meta)), |
|
55 Err(error) => match error.kind() { |
|
56 // TODO: when we require a Rust version where `NotADirectory` is |
|
57 // stable, invert this logic and return None for it and `NotFound` |
|
58 // and propagate any other error. |
|
59 ErrorKind::PermissionDenied => Err(error).with_context(|| { |
|
60 IoErrorContext::ReadingMetadata(path.to_owned()) |
|
61 }), |
|
62 _ => Ok(None), |
|
63 }, |
|
64 } |
|
65 } |
|
66 |
|
67 pub(crate) fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> { |
|
68 Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir())) |
|
69 } |
|
70 |
|
71 pub(crate) fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> { |
|
72 Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file())) |
|
73 } |