Show More
@@ -1,121 +1,133 b'' | |||||
1 | use std::fmt; |
|
1 | use std::fmt; | |
2 |
|
2 | |||
3 | /// Common error cases that can happen in many different APIs |
|
3 | /// Common error cases that can happen in many different APIs | |
4 | #[derive(Debug)] |
|
4 | #[derive(Debug)] | |
5 | pub enum HgError { |
|
5 | pub enum HgError { | |
6 | IoError { |
|
6 | IoError { | |
7 | error: std::io::Error, |
|
7 | error: std::io::Error, | |
8 | context: IoErrorContext, |
|
8 | context: IoErrorContext, | |
9 | }, |
|
9 | }, | |
10 |
|
10 | |||
11 | /// A file under `.hg/` normally only written by Mercurial |
|
11 | /// A file under `.hg/` normally only written by Mercurial is not in the | |
|
12 | /// expected format. This indicates a bug in Mercurial, filesystem | |||
|
13 | /// corruption, or hardware failure. | |||
12 | /// |
|
14 | /// | |
13 | /// The given string is a short explanation for users, not intended to be |
|
15 | /// The given string is a short explanation for users, not intended to be | |
14 | /// machine-readable. |
|
16 | /// machine-readable. | |
15 | CorruptedRepository(String), |
|
17 | CorruptedRepository(String), | |
16 |
|
18 | |||
17 | /// The respository or requested operation involves a feature not |
|
19 | /// The respository or requested operation involves a feature not | |
18 | /// supported by the Rust implementation. Falling back to the Python |
|
20 | /// supported by the Rust implementation. Falling back to the Python | |
19 | /// implementation may or may not work. |
|
21 | /// implementation may or may not work. | |
20 | /// |
|
22 | /// | |
21 | /// The given string is a short explanation for users, not intended to be |
|
23 | /// The given string is a short explanation for users, not intended to be | |
22 | /// machine-readable. |
|
24 | /// machine-readable. | |
23 | UnsupportedFeature(String), |
|
25 | UnsupportedFeature(String), | |
|
26 | ||||
|
27 | /// Operation cannot proceed for some other reason. | |||
|
28 | /// | |||
|
29 | /// The given string is a short explanation for users, not intended to be | |||
|
30 | /// machine-readable. | |||
|
31 | Abort(String), | |||
24 | } |
|
32 | } | |
25 |
|
33 | |||
26 | /// Details about where an I/O error happened |
|
34 | /// Details about where an I/O error happened | |
27 | #[derive(Debug, derive_more::From)] |
|
35 | #[derive(Debug, derive_more::From)] | |
28 | pub enum IoErrorContext { |
|
36 | pub enum IoErrorContext { | |
29 | /// A filesystem operation for the given file |
|
37 | /// A filesystem operation for the given file | |
30 | #[from] |
|
38 | #[from] | |
31 | File(std::path::PathBuf), |
|
39 | File(std::path::PathBuf), | |
32 | /// `std::env::current_dir` |
|
40 | /// `std::env::current_dir` | |
33 | CurrentDir, |
|
41 | CurrentDir, | |
34 | /// `std::env::current_exe` |
|
42 | /// `std::env::current_exe` | |
35 | CurrentExe, |
|
43 | CurrentExe, | |
36 | } |
|
44 | } | |
37 |
|
45 | |||
38 | impl HgError { |
|
46 | impl HgError { | |
39 | pub fn corrupted(explanation: impl Into<String>) -> Self { |
|
47 | pub fn corrupted(explanation: impl Into<String>) -> Self { | |
40 | // TODO: capture a backtrace here and keep it in the error value |
|
48 | // TODO: capture a backtrace here and keep it in the error value | |
41 | // to aid debugging? |
|
49 | // to aid debugging? | |
42 | // https://doc.rust-lang.org/std/backtrace/struct.Backtrace.html |
|
50 | // https://doc.rust-lang.org/std/backtrace/struct.Backtrace.html | |
43 | HgError::CorruptedRepository(explanation.into()) |
|
51 | HgError::CorruptedRepository(explanation.into()) | |
44 | } |
|
52 | } | |
45 |
|
53 | |||
46 | pub fn unsupported(explanation: impl Into<String>) -> Self { |
|
54 | pub fn unsupported(explanation: impl Into<String>) -> Self { | |
47 | HgError::UnsupportedFeature(explanation.into()) |
|
55 | HgError::UnsupportedFeature(explanation.into()) | |
48 | } |
|
56 | } | |
|
57 | pub fn abort(explanation: impl Into<String>) -> Self { | |||
|
58 | HgError::Abort(explanation.into()) | |||
|
59 | } | |||
49 | } |
|
60 | } | |
50 |
|
61 | |||
51 | // TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly? |
|
62 | // TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly? | |
52 | impl fmt::Display for HgError { |
|
63 | impl fmt::Display for HgError { | |
53 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
54 | match self { |
|
65 | match self { | |
55 | HgError::IoError { error, context } => { |
|
66 | HgError::IoError { error, context } => { | |
56 | write!(f, "{}: {}", error, context) |
|
67 | write!(f, "{}: {}", error, context) | |
57 | } |
|
68 | } | |
58 | HgError::CorruptedRepository(explanation) => { |
|
69 | HgError::CorruptedRepository(explanation) => { | |
59 | write!(f, "corrupted repository: {}", explanation) |
|
70 | write!(f, "corrupted repository: {}", explanation) | |
60 | } |
|
71 | } | |
61 | HgError::UnsupportedFeature(explanation) => { |
|
72 | HgError::UnsupportedFeature(explanation) => { | |
62 | write!(f, "unsupported feature: {}", explanation) |
|
73 | write!(f, "unsupported feature: {}", explanation) | |
63 | } |
|
74 | } | |
|
75 | HgError::Abort(explanation) => explanation.fmt(f), | |||
64 | } |
|
76 | } | |
65 | } |
|
77 | } | |
66 | } |
|
78 | } | |
67 |
|
79 | |||
68 | // TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly? |
|
80 | // TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly? | |
69 | impl fmt::Display for IoErrorContext { |
|
81 | impl fmt::Display for IoErrorContext { | |
70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
82 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
71 | match self { |
|
83 | match self { | |
72 | IoErrorContext::File(path) => path.display().fmt(f), |
|
84 | IoErrorContext::File(path) => path.display().fmt(f), | |
73 | IoErrorContext::CurrentDir => f.write_str("current directory"), |
|
85 | IoErrorContext::CurrentDir => f.write_str("current directory"), | |
74 | IoErrorContext::CurrentExe => f.write_str("current executable"), |
|
86 | IoErrorContext::CurrentExe => f.write_str("current executable"), | |
75 | } |
|
87 | } | |
76 | } |
|
88 | } | |
77 | } |
|
89 | } | |
78 |
|
90 | |||
79 | pub trait IoResultExt<T> { |
|
91 | pub trait IoResultExt<T> { | |
80 | /// Annotate a possible I/O error as related to a file at the given path. |
|
92 | /// Annotate a possible I/O error as related to a file at the given path. | |
81 | /// |
|
93 | /// | |
82 | /// This allows printing something like βFile not found: example.txtβ |
|
94 | /// This allows printing something like βFile not found: example.txtβ | |
83 | /// instead of just βFile not foundβ. |
|
95 | /// instead of just βFile not foundβ. | |
84 | /// |
|
96 | /// | |
85 | /// Converts a `Result` with `std::io::Error` into one with `HgError`. |
|
97 | /// Converts a `Result` with `std::io::Error` into one with `HgError`. | |
86 | fn for_file(self, path: &std::path::Path) -> Result<T, HgError>; |
|
98 | fn for_file(self, path: &std::path::Path) -> Result<T, HgError>; | |
87 | } |
|
99 | } | |
88 |
|
100 | |||
89 | impl<T> IoResultExt<T> for std::io::Result<T> { |
|
101 | impl<T> IoResultExt<T> for std::io::Result<T> { | |
90 | fn for_file(self, path: &std::path::Path) -> Result<T, HgError> { |
|
102 | fn for_file(self, path: &std::path::Path) -> Result<T, HgError> { | |
91 | self.map_err(|error| HgError::IoError { |
|
103 | self.map_err(|error| HgError::IoError { | |
92 | error, |
|
104 | error, | |
93 | context: IoErrorContext::File(path.to_owned()), |
|
105 | context: IoErrorContext::File(path.to_owned()), | |
94 | }) |
|
106 | }) | |
95 | } |
|
107 | } | |
96 | } |
|
108 | } | |
97 |
|
109 | |||
98 | pub trait HgResultExt<T> { |
|
110 | pub trait HgResultExt<T> { | |
99 | /// Handle missing files separately from other I/O error cases. |
|
111 | /// Handle missing files separately from other I/O error cases. | |
100 | /// |
|
112 | /// | |
101 | /// Wraps the `Ok` type in an `Option`: |
|
113 | /// Wraps the `Ok` type in an `Option`: | |
102 | /// |
|
114 | /// | |
103 | /// * `Ok(x)` becomes `Ok(Some(x))` |
|
115 | /// * `Ok(x)` becomes `Ok(Some(x))` | |
104 | /// * An I/O "not found" error becomes `Ok(None)` |
|
116 | /// * An I/O "not found" error becomes `Ok(None)` | |
105 | /// * Other errors are unchanged |
|
117 | /// * Other errors are unchanged | |
106 | fn io_not_found_as_none(self) -> Result<Option<T>, HgError>; |
|
118 | fn io_not_found_as_none(self) -> Result<Option<T>, HgError>; | |
107 | } |
|
119 | } | |
108 |
|
120 | |||
109 | impl<T> HgResultExt<T> for Result<T, HgError> { |
|
121 | impl<T> HgResultExt<T> for Result<T, HgError> { | |
110 | fn io_not_found_as_none(self) -> Result<Option<T>, HgError> { |
|
122 | fn io_not_found_as_none(self) -> Result<Option<T>, HgError> { | |
111 | match self { |
|
123 | match self { | |
112 | Ok(x) => Ok(Some(x)), |
|
124 | Ok(x) => Ok(Some(x)), | |
113 | Err(HgError::IoError { error, .. }) |
|
125 | Err(HgError::IoError { error, .. }) | |
114 | if error.kind() == std::io::ErrorKind::NotFound => |
|
126 | if error.kind() == std::io::ErrorKind::NotFound => | |
115 | { |
|
127 | { | |
116 | Ok(None) |
|
128 | Ok(None) | |
117 | } |
|
129 | } | |
118 | Err(other_error) => Err(other_error), |
|
130 | Err(other_error) => Err(other_error), | |
119 | } |
|
131 | } | |
120 | } |
|
132 | } | |
121 | } |
|
133 | } |
@@ -1,171 +1,186 b'' | |||||
1 | use crate::config::Config; |
|
1 | use crate::config::Config; | |
2 | use crate::errors::{HgError, IoResultExt}; |
|
2 | use crate::errors::{HgError, IoResultExt}; | |
3 | use crate::requirements; |
|
3 | use crate::requirements; | |
4 | use crate::utils::files::get_path_from_bytes; |
|
4 | use crate::utils::files::get_path_from_bytes; | |
5 | use memmap::{Mmap, MmapOptions}; |
|
5 | use memmap::{Mmap, MmapOptions}; | |
6 | use std::collections::HashSet; |
|
6 | use std::collections::HashSet; | |
7 | use std::path::{Path, PathBuf}; |
|
7 | use std::path::{Path, PathBuf}; | |
8 |
|
8 | |||
9 | /// A repository on disk |
|
9 | /// A repository on disk | |
10 | pub struct Repo { |
|
10 | pub struct Repo { | |
11 | working_directory: PathBuf, |
|
11 | working_directory: PathBuf, | |
12 | dot_hg: PathBuf, |
|
12 | dot_hg: PathBuf, | |
13 | store: PathBuf, |
|
13 | store: PathBuf, | |
14 | requirements: HashSet<String>, |
|
14 | requirements: HashSet<String>, | |
15 | } |
|
15 | } | |
16 |
|
16 | |||
17 | #[derive(Debug, derive_more::From)] |
|
17 | #[derive(Debug, derive_more::From)] | |
18 | pub enum RepoFindError { |
|
18 | pub enum RepoFindError { | |
19 | NotFoundInCurrentDirectoryOrAncestors { |
|
19 | NotFoundInCurrentDirectoryOrAncestors { | |
20 | current_directory: PathBuf, |
|
20 | current_directory: PathBuf, | |
21 | }, |
|
21 | }, | |
22 | #[from] |
|
22 | #[from] | |
23 | Other(HgError), |
|
23 | Other(HgError), | |
24 | } |
|
24 | } | |
25 |
|
25 | |||
26 | /// Filesystem access abstraction for the contents of a given "base" diretory |
|
26 | /// Filesystem access abstraction for the contents of a given "base" diretory | |
27 | #[derive(Clone, Copy)] |
|
27 | #[derive(Clone, Copy)] | |
28 | pub(crate) struct Vfs<'a> { |
|
28 | pub(crate) struct Vfs<'a> { | |
29 | base: &'a Path, |
|
29 | base: &'a Path, | |
30 | } |
|
30 | } | |
31 |
|
31 | |||
32 | impl Repo { |
|
32 | impl Repo { | |
33 | /// Search the current directory and its ancestores for a repository: |
|
33 | /// Search the current directory and its ancestores for a repository: | |
34 | /// a working directory that contains a `.hg` sub-directory. |
|
34 | /// a working directory that contains a `.hg` sub-directory. | |
35 |
pub fn find( |
|
35 | pub fn find(config: &Config) -> Result<Self, RepoFindError> { | |
36 | let current_directory = crate::utils::current_dir()?; |
|
36 | let current_directory = crate::utils::current_dir()?; | |
37 | // ancestors() is inclusive: it first yields `current_directory` as-is. |
|
37 | // ancestors() is inclusive: it first yields `current_directory` as-is. | |
38 | for ancestor in current_directory.ancestors() { |
|
38 | for ancestor in current_directory.ancestors() { | |
39 | if ancestor.join(".hg").is_dir() { |
|
39 | if ancestor.join(".hg").is_dir() { | |
40 | return Ok(Self::new_at_path(ancestor.to_owned())?); |
|
40 | return Ok(Self::new_at_path(ancestor.to_owned(), config)?); | |
41 | } |
|
41 | } | |
42 | } |
|
42 | } | |
43 | Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors { |
|
43 | Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors { | |
44 | current_directory, |
|
44 | current_directory, | |
45 | }) |
|
45 | }) | |
46 | } |
|
46 | } | |
47 |
|
47 | |||
48 | /// To be called after checking that `.hg` is a sub-directory |
|
48 | /// To be called after checking that `.hg` is a sub-directory | |
49 | fn new_at_path(working_directory: PathBuf) -> Result<Self, HgError> { |
|
49 | fn new_at_path( | |
|
50 | working_directory: PathBuf, | |||
|
51 | config: &Config, | |||
|
52 | ) -> Result<Self, HgError> { | |||
50 | let dot_hg = working_directory.join(".hg"); |
|
53 | let dot_hg = working_directory.join(".hg"); | |
51 |
|
54 | |||
52 | let hg_vfs = Vfs { base: &dot_hg }; |
|
55 | let hg_vfs = Vfs { base: &dot_hg }; | |
53 | let mut reqs = requirements::load_if_exists(hg_vfs)?; |
|
56 | let mut reqs = requirements::load_if_exists(hg_vfs)?; | |
54 | let relative = |
|
57 | let relative = | |
55 | reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT); |
|
58 | reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT); | |
56 | let shared = |
|
59 | let shared = | |
57 | reqs.contains(requirements::SHARED_REQUIREMENT) || relative; |
|
60 | reqs.contains(requirements::SHARED_REQUIREMENT) || relative; | |
58 |
|
61 | |||
59 | // From `mercurial/localrepo.py`: |
|
62 | // From `mercurial/localrepo.py`: | |
60 | // |
|
63 | // | |
61 | // if .hg/requires contains the sharesafe requirement, it means |
|
64 | // if .hg/requires contains the sharesafe requirement, it means | |
62 | // there exists a `.hg/store/requires` too and we should read it |
|
65 | // there exists a `.hg/store/requires` too and we should read it | |
63 | // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement |
|
66 | // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement | |
64 | // is present. We never write SHARESAFE_REQUIREMENT for a repo if store |
|
67 | // is present. We never write SHARESAFE_REQUIREMENT for a repo if store | |
65 | // is not present, refer checkrequirementscompat() for that |
|
68 | // is not present, refer checkrequirementscompat() for that | |
66 | // |
|
69 | // | |
67 | // However, if SHARESAFE_REQUIREMENT is not present, it means that the |
|
70 | // However, if SHARESAFE_REQUIREMENT is not present, it means that the | |
68 | // repository was shared the old way. We check the share source |
|
71 | // repository was shared the old way. We check the share source | |
69 | // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the |
|
72 | // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the | |
70 | // current repository needs to be reshared |
|
73 | // current repository needs to be reshared | |
71 | let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT); |
|
74 | let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT); | |
72 |
|
75 | |||
73 | let store_path; |
|
76 | let store_path; | |
74 | if !shared { |
|
77 | if !shared { | |
75 | store_path = dot_hg.join("store"); |
|
78 | store_path = dot_hg.join("store"); | |
76 | if share_safe { |
|
79 | if share_safe { | |
77 | reqs.extend(requirements::load(Vfs { base: &store_path })?); |
|
80 | reqs.extend(requirements::load(Vfs { base: &store_path })?); | |
78 | } |
|
81 | } | |
79 | } else { |
|
82 | } else { | |
80 | let bytes = hg_vfs.read("sharedpath")?; |
|
83 | let bytes = hg_vfs.read("sharedpath")?; | |
81 | let mut shared_path = get_path_from_bytes(&bytes).to_owned(); |
|
84 | let mut shared_path = get_path_from_bytes(&bytes).to_owned(); | |
82 | if relative { |
|
85 | if relative { | |
83 | shared_path = dot_hg.join(shared_path) |
|
86 | shared_path = dot_hg.join(shared_path) | |
84 | } |
|
87 | } | |
85 | if !shared_path.is_dir() { |
|
88 | if !shared_path.is_dir() { | |
86 | return Err(HgError::corrupted(format!( |
|
89 | return Err(HgError::corrupted(format!( | |
87 | ".hg/sharedpath points to nonexistent directory {}", |
|
90 | ".hg/sharedpath points to nonexistent directory {}", | |
88 | shared_path.display() |
|
91 | shared_path.display() | |
89 | ))); |
|
92 | ))); | |
90 | } |
|
93 | } | |
91 |
|
94 | |||
92 | store_path = shared_path.join("store"); |
|
95 | store_path = shared_path.join("store"); | |
93 |
|
96 | |||
94 | let source_is_share_safe = |
|
97 | let source_is_share_safe = | |
95 | requirements::load(Vfs { base: &shared_path })? |
|
98 | requirements::load(Vfs { base: &shared_path })? | |
96 | .contains(requirements::SHARESAFE_REQUIREMENT); |
|
99 | .contains(requirements::SHARESAFE_REQUIREMENT); | |
97 |
|
100 | |||
98 | // TODO: support for `share.safe-mismatch.*` config |
|
|||
99 | if share_safe && !source_is_share_safe { |
|
101 | if share_safe && !source_is_share_safe { | |
100 | return Err(HgError::unsupported("share-safe downgrade")); |
|
102 | return Err(match config.get(b"safe-mismatch", b"source-not-safe") { | |
|
103 | Some(b"abort") | None => HgError::abort( | |||
|
104 | "share source does not support share-safe requirement" | |||
|
105 | ), | |||
|
106 | _ => HgError::unsupported("share-safe downgrade") | |||
|
107 | }); | |||
101 | } else if source_is_share_safe && !share_safe { |
|
108 | } else if source_is_share_safe && !share_safe { | |
102 | return Err(HgError::unsupported("share-safe upgrade")); |
|
109 | return Err( | |
|
110 | match config.get(b"safe-mismatch", b"source-safe") { | |||
|
111 | Some(b"abort") | None => HgError::abort( | |||
|
112 | "version mismatch: source uses share-safe \ | |||
|
113 | functionality while the current share does not", | |||
|
114 | ), | |||
|
115 | _ => HgError::unsupported("share-safe upgrade"), | |||
|
116 | }, | |||
|
117 | ); | |||
103 | } |
|
118 | } | |
104 | } |
|
119 | } | |
105 |
|
120 | |||
106 | let repo = Self { |
|
121 | let repo = Self { | |
107 | requirements: reqs, |
|
122 | requirements: reqs, | |
108 | working_directory, |
|
123 | working_directory, | |
109 | store: store_path, |
|
124 | store: store_path, | |
110 | dot_hg, |
|
125 | dot_hg, | |
111 | }; |
|
126 | }; | |
112 |
|
127 | |||
113 | requirements::check(&repo)?; |
|
128 | requirements::check(&repo)?; | |
114 |
|
129 | |||
115 | Ok(repo) |
|
130 | Ok(repo) | |
116 | } |
|
131 | } | |
117 |
|
132 | |||
118 | pub fn working_directory_path(&self) -> &Path { |
|
133 | pub fn working_directory_path(&self) -> &Path { | |
119 | &self.working_directory |
|
134 | &self.working_directory | |
120 | } |
|
135 | } | |
121 |
|
136 | |||
122 | pub fn requirements(&self) -> &HashSet<String> { |
|
137 | pub fn requirements(&self) -> &HashSet<String> { | |
123 | &self.requirements |
|
138 | &self.requirements | |
124 | } |
|
139 | } | |
125 |
|
140 | |||
126 | /// For accessing repository files (in `.hg`), except for the store |
|
141 | /// For accessing repository files (in `.hg`), except for the store | |
127 | /// (`.hg/store`). |
|
142 | /// (`.hg/store`). | |
128 | pub(crate) fn hg_vfs(&self) -> Vfs<'_> { |
|
143 | pub(crate) fn hg_vfs(&self) -> Vfs<'_> { | |
129 | Vfs { base: &self.dot_hg } |
|
144 | Vfs { base: &self.dot_hg } | |
130 | } |
|
145 | } | |
131 |
|
146 | |||
132 | /// For accessing repository store files (in `.hg/store`) |
|
147 | /// For accessing repository store files (in `.hg/store`) | |
133 | pub(crate) fn store_vfs(&self) -> Vfs<'_> { |
|
148 | pub(crate) fn store_vfs(&self) -> Vfs<'_> { | |
134 | Vfs { base: &self.store } |
|
149 | Vfs { base: &self.store } | |
135 | } |
|
150 | } | |
136 |
|
151 | |||
137 | /// For accessing the working copy |
|
152 | /// For accessing the working copy | |
138 |
|
153 | |||
139 | // The undescore prefix silences the "never used" warning. Remove before |
|
154 | // The undescore prefix silences the "never used" warning. Remove before | |
140 | // using. |
|
155 | // using. | |
141 | pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> { |
|
156 | pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> { | |
142 | Vfs { |
|
157 | Vfs { | |
143 | base: &self.working_directory, |
|
158 | base: &self.working_directory, | |
144 | } |
|
159 | } | |
145 | } |
|
160 | } | |
146 | } |
|
161 | } | |
147 |
|
162 | |||
148 | impl Vfs<'_> { |
|
163 | impl Vfs<'_> { | |
149 | pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf { |
|
164 | pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf { | |
150 | self.base.join(relative_path) |
|
165 | self.base.join(relative_path) | |
151 | } |
|
166 | } | |
152 |
|
167 | |||
153 | pub(crate) fn read( |
|
168 | pub(crate) fn read( | |
154 | &self, |
|
169 | &self, | |
155 | relative_path: impl AsRef<Path>, |
|
170 | relative_path: impl AsRef<Path>, | |
156 | ) -> Result<Vec<u8>, HgError> { |
|
171 | ) -> Result<Vec<u8>, HgError> { | |
157 | let path = self.join(relative_path); |
|
172 | let path = self.join(relative_path); | |
158 | std::fs::read(&path).for_file(&path) |
|
173 | std::fs::read(&path).for_file(&path) | |
159 | } |
|
174 | } | |
160 |
|
175 | |||
161 | pub(crate) fn mmap_open( |
|
176 | pub(crate) fn mmap_open( | |
162 | &self, |
|
177 | &self, | |
163 | relative_path: impl AsRef<Path>, |
|
178 | relative_path: impl AsRef<Path>, | |
164 | ) -> Result<Mmap, HgError> { |
|
179 | ) -> Result<Mmap, HgError> { | |
165 | let path = self.base.join(relative_path); |
|
180 | let path = self.base.join(relative_path); | |
166 | let file = std::fs::File::open(&path).for_file(&path)?; |
|
181 | let file = std::fs::File::open(&path).for_file(&path)?; | |
167 | // TODO: what are the safety requirements here? |
|
182 | // TODO: what are the safety requirements here? | |
168 | let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?; |
|
183 | let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?; | |
169 | Ok(mmap) |
|
184 | Ok(mmap) | |
170 | } |
|
185 | } | |
171 | } |
|
186 | } |
General Comments 0
You need to be logged in to leave comments.
Login now