##// END OF EJS Templates
rust: Introduce an `HgError` enum for common error cases...
Simon Sapin -
r47167:39e94078 default
parent child Browse files
Show More
@@ -0,0 +1,111
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 }
@@ -1,156 +1,157
1 1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 2 // and Mercurial contributors
3 3 //
4 4 // This software may be used and distributed according to the terms of the
5 5 // GNU General Public License version 2 or any later version.
6 6 mod ancestors;
7 7 pub mod dagops;
8 pub mod errors;
8 9 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
9 10 mod dirstate;
10 11 pub mod discovery;
11 12 pub mod requirements;
12 13 pub mod testing; // unconditionally built, for use from integration tests
13 14 pub use dirstate::{
14 15 dirs_multiset::{DirsMultiset, DirsMultisetIter},
15 16 dirstate_map::DirstateMap,
16 17 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
17 18 status::{
18 19 status, BadMatch, BadType, DirstateStatus, StatusError, StatusOptions,
19 20 },
20 21 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
21 22 StateMap, StateMapIter,
22 23 };
23 24 pub mod copy_tracing;
24 25 mod filepatterns;
25 26 pub mod matchers;
26 27 pub mod repo;
27 28 pub mod revlog;
28 29 pub use revlog::*;
29 30 pub mod config;
30 31 pub mod operations;
31 32 pub mod revset;
32 33 pub mod utils;
33 34
34 35 use crate::utils::hg_path::{HgPathBuf, HgPathError};
35 36 pub use filepatterns::{
36 37 parse_pattern_syntax, read_pattern_file, IgnorePattern,
37 38 PatternFileWarning, PatternSyntax,
38 39 };
39 40 use std::collections::HashMap;
40 41 use twox_hash::RandomXxHashBuilder64;
41 42
42 43 /// This is a contract between the `micro-timer` crate and us, to expose
43 44 /// the `log` crate as `crate::log`.
44 45 use log;
45 46
46 47 pub type LineNumber = usize;
47 48
48 49 /// Rust's default hasher is too slow because it tries to prevent collision
49 50 /// attacks. We are not concerned about those: if an ill-minded person has
50 51 /// write access to your repository, you have other issues.
51 52 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
52 53
53 54 #[derive(Clone, Debug, PartialEq)]
54 55 pub enum DirstateParseError {
55 56 TooLittleData,
56 57 Overflow,
57 58 // TODO refactor to use bytes instead of String
58 59 CorruptedEntry(String),
59 60 Damaged,
60 61 }
61 62
62 63 impl From<std::io::Error> for DirstateParseError {
63 64 fn from(e: std::io::Error) -> Self {
64 65 DirstateParseError::CorruptedEntry(e.to_string())
65 66 }
66 67 }
67 68
68 69 impl ToString for DirstateParseError {
69 70 fn to_string(&self) -> String {
70 71 use crate::DirstateParseError::*;
71 72 match self {
72 73 TooLittleData => "Too little data for dirstate.".to_string(),
73 74 Overflow => "Overflow in dirstate.".to_string(),
74 75 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
75 76 Damaged => "Dirstate appears to be damaged.".to_string(),
76 77 }
77 78 }
78 79 }
79 80
80 81 #[derive(Debug, PartialEq)]
81 82 pub enum DirstatePackError {
82 83 CorruptedEntry(String),
83 84 CorruptedParent,
84 85 BadSize(usize, usize),
85 86 }
86 87
87 88 impl From<std::io::Error> for DirstatePackError {
88 89 fn from(e: std::io::Error) -> Self {
89 90 DirstatePackError::CorruptedEntry(e.to_string())
90 91 }
91 92 }
92 93
93 94 #[derive(Debug, PartialEq)]
94 95 pub enum DirstateMapError {
95 96 PathNotFound(HgPathBuf),
96 97 EmptyPath,
97 98 InvalidPath(HgPathError),
98 99 }
99 100
100 101 impl ToString for DirstateMapError {
101 102 fn to_string(&self) -> String {
102 103 match self {
103 104 DirstateMapError::PathNotFound(_) => {
104 105 "expected a value, found none".to_string()
105 106 }
106 107 DirstateMapError::EmptyPath => "Overflow in dirstate.".to_string(),
107 108 DirstateMapError::InvalidPath(e) => e.to_string(),
108 109 }
109 110 }
110 111 }
111 112
112 113 #[derive(Debug, derive_more::From)]
113 114 pub enum DirstateError {
114 115 Parse(DirstateParseError),
115 116 Pack(DirstatePackError),
116 117 Map(DirstateMapError),
117 118 IO(std::io::Error),
118 119 }
119 120
120 121 #[derive(Debug, derive_more::From)]
121 122 pub enum PatternError {
122 123 #[from]
123 124 Path(HgPathError),
124 125 UnsupportedSyntax(String),
125 126 UnsupportedSyntaxInFile(String, String, usize),
126 127 TooLong(usize),
127 128 #[from]
128 129 IO(std::io::Error),
129 130 /// Needed a pattern that can be turned into a regex but got one that
130 131 /// can't. This should only happen through programmer error.
131 132 NonRegexPattern(IgnorePattern),
132 133 }
133 134
134 135 impl ToString for PatternError {
135 136 fn to_string(&self) -> String {
136 137 match self {
137 138 PatternError::UnsupportedSyntax(syntax) => {
138 139 format!("Unsupported syntax {}", syntax)
139 140 }
140 141 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
141 142 format!(
142 143 "{}:{}: unsupported syntax {}",
143 144 file_path, line, syntax
144 145 )
145 146 }
146 147 PatternError::TooLong(size) => {
147 148 format!("matcher pattern is too long ({} bytes)", size)
148 149 }
149 150 PatternError::IO(e) => e.to_string(),
150 151 PatternError::Path(e) => e.to_string(),
151 152 PatternError::NonRegexPattern(pattern) => {
152 153 format!("'{:?}' cannot be turned into a regex", pattern)
153 154 }
154 155 }
155 156 }
156 157 }
General Comments 0
You need to be logged in to leave comments. Login now