Show More
@@ -47,6 +47,8 b' pub enum HgError {' | |||||
47 | /// Details about where an I/O error happened |
|
47 | /// Details about where an I/O error happened | |
48 | #[derive(Debug)] |
|
48 | #[derive(Debug)] | |
49 | pub enum IoErrorContext { |
|
49 | pub enum IoErrorContext { | |
|
50 | /// `std::fs::metadata` | |||
|
51 | ReadingMetadata(std::path::PathBuf), | |||
50 | ReadingFile(std::path::PathBuf), |
|
52 | ReadingFile(std::path::PathBuf), | |
51 | WritingFile(std::path::PathBuf), |
|
53 | WritingFile(std::path::PathBuf), | |
52 | RemovingFile(std::path::PathBuf), |
|
54 | RemovingFile(std::path::PathBuf), | |
@@ -108,6 +110,9 b' impl fmt::Display for HgError {' | |||||
108 | impl fmt::Display for IoErrorContext { |
|
110 | impl fmt::Display for IoErrorContext { | |
109 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
111 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
110 | match self { |
|
112 | match self { | |
|
113 | IoErrorContext::ReadingMetadata(path) => { | |||
|
114 | write!(f, "when reading metadata of {}", path.display()) | |||
|
115 | } | |||
111 | IoErrorContext::ReadingFile(path) => { |
|
116 | IoErrorContext::ReadingFile(path) => { | |
112 | write!(f, "when reading {}", path.display()) |
|
117 | write!(f, "when reading {}", path.display()) | |
113 | } |
|
118 | } |
@@ -6,6 +6,7 b' use crate::utils::files::get_path_from_b' | |||||
6 | use crate::utils::SliceExt; |
|
6 | use crate::utils::SliceExt; | |
7 | use memmap::{Mmap, MmapOptions}; |
|
7 | use memmap::{Mmap, MmapOptions}; | |
8 | use std::collections::HashSet; |
|
8 | use std::collections::HashSet; | |
|
9 | use std::io::ErrorKind; | |||
9 | use std::path::{Path, PathBuf}; |
|
10 | use std::path::{Path, PathBuf}; | |
10 |
|
11 | |||
11 | /// A repository on disk |
|
12 | /// A repository on disk | |
@@ -51,7 +52,7 b' impl Repo {' | |||||
51 | // ancestors() is inclusive: it first yields `current_directory` |
|
52 | // ancestors() is inclusive: it first yields `current_directory` | |
52 | // as-is. |
|
53 | // as-is. | |
53 | for ancestor in current_directory.ancestors() { |
|
54 | for ancestor in current_directory.ancestors() { | |
54 |
if ancestor.join(".hg") |
|
55 | if is_dir(ancestor.join(".hg"))? { | |
55 | return Ok(ancestor.to_path_buf()); |
|
56 | return Ok(ancestor.to_path_buf()); | |
56 | } |
|
57 | } | |
57 | } |
|
58 | } | |
@@ -73,9 +74,9 b' impl Repo {' | |||||
73 | explicit_path: Option<PathBuf>, |
|
74 | explicit_path: Option<PathBuf>, | |
74 | ) -> Result<Self, RepoError> { |
|
75 | ) -> Result<Self, RepoError> { | |
75 | if let Some(root) = explicit_path { |
|
76 | if let Some(root) = explicit_path { | |
76 |
if root.join(".hg") |
|
77 | if is_dir(root.join(".hg"))? { | |
77 | Self::new_at_path(root.to_owned(), config) |
|
78 | Self::new_at_path(root.to_owned(), config) | |
78 |
} else if |
|
79 | } else if is_file(&root)? { | |
79 | Err(HgError::unsupported("bundle repository").into()) |
|
80 | Err(HgError::unsupported("bundle repository").into()) | |
80 | } else { |
|
81 | } else { | |
81 | Err(RepoError::NotFound { |
|
82 | Err(RepoError::NotFound { | |
@@ -130,7 +131,7 b' impl Repo {' | |||||
130 | if relative { |
|
131 | if relative { | |
131 | shared_path = dot_hg.join(shared_path) |
|
132 | shared_path = dot_hg.join(shared_path) | |
132 | } |
|
133 | } | |
133 |
if ! |
|
134 | if !is_dir(&shared_path)? { | |
134 | return Err(HgError::corrupted(format!( |
|
135 | return Err(HgError::corrupted(format!( | |
135 | ".hg/sharedpath points to nonexistent directory {}", |
|
136 | ".hg/sharedpath points to nonexistent directory {}", | |
136 | shared_path.display() |
|
137 | shared_path.display() | |
@@ -286,3 +287,29 b" impl Vfs<'_> {" | |||||
286 | .with_context(|| IoErrorContext::RenamingFile { from, to }) |
|
287 | .with_context(|| IoErrorContext::RenamingFile { from, to }) | |
287 | } |
|
288 | } | |
288 | } |
|
289 | } | |
|
290 | ||||
|
291 | fn fs_metadata( | |||
|
292 | path: impl AsRef<Path>, | |||
|
293 | ) -> Result<Option<std::fs::Metadata>, HgError> { | |||
|
294 | let path = path.as_ref(); | |||
|
295 | match std::fs::metadata(path) { | |||
|
296 | Ok(meta) => Ok(Some(meta)), | |||
|
297 | Err(error) => match error.kind() { | |||
|
298 | // TODO: when we require a Rust version where `NotADirectory` is | |||
|
299 | // stable, invert this logic and return None for it and `NotFound` | |||
|
300 | // and propagate any other error. | |||
|
301 | ErrorKind::PermissionDenied => Err(error).with_context(|| { | |||
|
302 | IoErrorContext::ReadingMetadata(path.to_owned()) | |||
|
303 | }), | |||
|
304 | _ => Ok(None), | |||
|
305 | }, | |||
|
306 | } | |||
|
307 | } | |||
|
308 | ||||
|
309 | fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> { | |||
|
310 | Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir())) | |||
|
311 | } | |||
|
312 | ||||
|
313 | fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> { | |||
|
314 | Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file())) | |||
|
315 | } |
General Comments 0
You need to be logged in to leave comments.
Login now