Mercurial > public > mercurial-scm > hg-stable
comparison rust/hg-core/src/errors.rs @ 46506:39e9407820ac
rust: Introduce an `HgError` enum for common error cases
Differential Revision: https://phab.mercurial-scm.org/D9892
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Wed, 27 Jan 2021 13:19:49 +0100 |
parents | |
children | 43d63979a75e |
comparison
equal
deleted
inserted
replaced
46505:b274aa2f20fd | 46506:39e9407820ac |
---|---|
1 use std::fmt; | |
2 | |
3 /// Common error cases that can happen in many different APIs | |
4 #[derive(Debug)] | |
5 pub enum HgError { | |
6 IoError { | |
7 error: std::io::Error, | |
8 context: IoErrorContext, | |
9 }, | |
10 | |
11 /// A file under `.hg/` normally only written by Mercurial | |
12 /// | |
13 /// The given string is a short explanation for users, not intended to be | |
14 /// machine-readable. | |
15 CorruptedRepository(String), | |
16 | |
17 /// The respository or requested operation involves a feature not | |
18 /// supported by the Rust implementation. Falling back to the Python | |
19 /// implementation may or may not work. | |
20 /// | |
21 /// The given string is a short explanation for users, not intended to be | |
22 /// machine-readable. | |
23 UnsupportedFeature(String), | |
24 } | |
25 | |
26 /// Details about where an I/O error happened | |
27 #[derive(Debug, derive_more::From)] | |
28 pub enum IoErrorContext { | |
29 /// A filesystem operation returned `std::io::Error` | |
30 #[from] | |
31 File(std::path::PathBuf), | |
32 /// `std::env::current_dir` returned `std::io::Error` | |
33 CurrentDir, | |
34 } | |
35 | |
36 impl HgError { | |
37 pub fn corrupted(explanation: impl Into<String>) -> Self { | |
38 HgError::CorruptedRepository(explanation.into()) | |
39 } | |
40 } | |
41 | |
42 // TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly? | |
43 impl fmt::Display for HgError { | |
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
45 match self { | |
46 HgError::IoError { error, context } => { | |
47 write!(f, "{}: {}", error, context) | |
48 } | |
49 HgError::CorruptedRepository(explanation) => { | |
50 write!(f, "corrupted repository: {}", explanation) | |
51 } | |
52 HgError::UnsupportedFeature(explanation) => { | |
53 write!(f, "unsupported feature: {}", explanation) | |
54 } | |
55 } | |
56 } | |
57 } | |
58 | |
59 // TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly? | |
60 impl fmt::Display for IoErrorContext { | |
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
62 match self { | |
63 IoErrorContext::File(path) => path.display().fmt(f), | |
64 IoErrorContext::CurrentDir => f.write_str("current directory"), | |
65 } | |
66 } | |
67 } | |
68 | |
69 pub trait IoResultExt<T> { | |
70 /// Annotate a possible I/O error as related to a file at the given path. | |
71 /// | |
72 /// This allows printing something like “File not found: example.txt” | |
73 /// instead of just “File not found”. | |
74 /// | |
75 /// Converts a `Result` with `std::io::Error` into one with `HgError`. | |
76 fn for_file(self, path: &std::path::Path) -> Result<T, HgError>; | |
77 } | |
78 | |
79 impl<T> IoResultExt<T> for std::io::Result<T> { | |
80 fn for_file(self, path: &std::path::Path) -> Result<T, HgError> { | |
81 self.map_err(|error| HgError::IoError { | |
82 error, | |
83 context: IoErrorContext::File(path.to_owned()), | |
84 }) | |
85 } | |
86 } | |
87 | |
88 pub trait HgResultExt<T> { | |
89 /// Handle missing files separately from other I/O error cases. | |
90 /// | |
91 /// Wraps the `Ok` type in an `Option`: | |
92 /// | |
93 /// * `Ok(x)` becomes `Ok(Some(x))` | |
94 /// * An I/O "not found" error becomes `Ok(None)` | |
95 /// * Other errors are unchanged | |
96 fn io_not_found_as_none(self) -> Result<Option<T>, HgError>; | |
97 } | |
98 | |
99 impl<T> HgResultExt<T> for Result<T, HgError> { | |
100 fn io_not_found_as_none(self) -> Result<Option<T>, HgError> { | |
101 match self { | |
102 Ok(x) => Ok(Some(x)), | |
103 Err(HgError::IoError { error, .. }) | |
104 if error.kind() == std::io::ErrorKind::NotFound => | |
105 { | |
106 Ok(None) | |
107 } | |
108 Err(other_error) => Err(other_error), | |
109 } | |
110 } | |
111 } |