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