Show More
@@ -1,3 +1,4 b'' | |||
|
1 | use crate::exitcode; | |
|
1 | 2 | use crate::ui::utf8_to_local; |
|
2 | 3 | use crate::ui::UiError; |
|
3 | 4 | use crate::NoRepoInCwdError; |
@@ -14,7 +15,10 b' use std::convert::From;' | |||
|
14 | 15 | #[derive(Debug)] |
|
15 | 16 | pub enum CommandError { |
|
16 | 17 | /// Exit with an error message and "standard" failure exit code. |
|
17 | Abort { message: Vec<u8> }, | |
|
18 | Abort { | |
|
19 | message: Vec<u8>, | |
|
20 | detailed_exit_code: exitcode::ExitCode, | |
|
21 | }, | |
|
18 | 22 | |
|
19 | 23 | /// Exit with a failure exit code but no message. |
|
20 | 24 | Unsuccessful, |
@@ -28,11 +32,19 b' pub enum CommandError {' | |||
|
28 | 32 | |
|
29 | 33 | impl CommandError { |
|
30 | 34 | pub fn abort(message: impl AsRef<str>) -> Self { |
|
35 | CommandError::abort_with_exit_code(message, exitcode::ABORT) | |
|
36 | } | |
|
37 | ||
|
38 | pub fn abort_with_exit_code( | |
|
39 | message: impl AsRef<str>, | |
|
40 | detailed_exit_code: exitcode::ExitCode, | |
|
41 | ) -> Self { | |
|
31 | 42 | CommandError::Abort { |
|
32 | 43 | // TODO: bytes-based (instead of Unicode-based) formatting |
|
33 | 44 | // of error messages to handle non-UTF-8 filenames etc: |
|
34 | 45 | // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output |
|
35 | 46 | message: utf8_to_local(message.as_ref()).into(), |
|
47 | detailed_exit_code: detailed_exit_code, | |
|
36 | 48 | } |
|
37 | 49 | } |
|
38 | 50 | |
@@ -64,7 +76,10 b' impl From<HgError> for CommandError {' | |||
|
64 | 76 | |
|
65 | 77 | impl From<ConfigValueParseError> for CommandError { |
|
66 | 78 | fn from(error: ConfigValueParseError) -> Self { |
|
67 |
CommandError::abort( |
|
|
79 | CommandError::abort_with_exit_code( | |
|
80 | error.to_string(), | |
|
81 | exitcode::CONFIG_ERROR_ABORT, | |
|
82 | ) | |
|
68 | 83 | } |
|
69 | 84 | } |
|
70 | 85 | |
@@ -85,6 +100,7 b' impl From<RepoError> for CommandError {' | |||
|
85 | 100 | b"abort: repository {} not found", |
|
86 | 101 | get_bytes_from_path(at) |
|
87 | 102 | ), |
|
103 | detailed_exit_code: exitcode::ABORT, | |
|
88 | 104 | }, |
|
89 | 105 | RepoError::ConfigParseError(error) => error.into(), |
|
90 | 106 | RepoError::Other(error) => error.into(), |
@@ -100,6 +116,7 b" impl<'a> From<&'a NoRepoInCwdError> for " | |||
|
100 | 116 | b"abort: no repository found in '{}' (.hg not found)!", |
|
101 | 117 | get_bytes_from_path(cwd) |
|
102 | 118 | ), |
|
119 | detailed_exit_code: exitcode::ABORT, | |
|
103 | 120 | } |
|
104 | 121 | } |
|
105 | 122 | } |
@@ -132,6 +149,7 b' impl From<ConfigParseError> for CommandE' | |||
|
132 | 149 | line_message, |
|
133 | 150 | message |
|
134 | 151 | ), |
|
152 | detailed_exit_code: exitcode::CONFIG_ERROR_ABORT, | |
|
135 | 153 | } |
|
136 | 154 | } |
|
137 | 155 | } |
@@ -6,6 +6,9 b' pub const OK: ExitCode = 0;' | |||
|
6 | 6 | /// Generic abort |
|
7 | 7 | pub const ABORT: ExitCode = 255; |
|
8 | 8 | |
|
9 | // Abort when there is a config related error | |
|
10 | pub const CONFIG_ERROR_ABORT: ExitCode = 30; | |
|
11 | ||
|
9 | 12 | /// Generic something completed but did not succeed |
|
10 | 13 | pub const UNSUCCESSFUL: ExitCode = 1; |
|
11 | 14 |
@@ -82,7 +82,14 b' fn main_with_result(' | |||
|
82 | 82 | let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?; |
|
83 | 83 | blackbox.log_command_start(); |
|
84 | 84 | let result = run(&invocation); |
|
85 |
blackbox.log_command_end(exit_code( |
|
|
85 | blackbox.log_command_end(exit_code( | |
|
86 | &result, | |
|
87 | // TODO: show a warning or combine with original error if `get_bool` | |
|
88 | // returns an error | |
|
89 | config | |
|
90 | .get_bool(b"ui", b"detailed-exit-code") | |
|
91 | .unwrap_or(false), | |
|
92 | )); | |
|
86 | 93 | result |
|
87 | 94 | } |
|
88 | 95 | |
@@ -114,6 +121,7 b' fn main() {' | |||
|
114 | 121 | error, |
|
115 | 122 | cwd.display() |
|
116 | 123 | ))), |
|
124 | false, | |
|
117 | 125 | ) |
|
118 | 126 | }) |
|
119 | 127 | }); |
@@ -125,7 +133,13 b' fn main() {' | |||
|
125 | 133 | // "unsupported" error but that is not enforced by the type system. |
|
126 | 134 | let on_unsupported = OnUnsupported::Abort; |
|
127 | 135 | |
|
128 | exit(&initial_current_dir, &ui, on_unsupported, Err(error.into())) | |
|
136 | exit( | |
|
137 | &initial_current_dir, | |
|
138 | &ui, | |
|
139 | on_unsupported, | |
|
140 | Err(error.into()), | |
|
141 | false, | |
|
142 | ) | |
|
129 | 143 | }); |
|
130 | 144 | |
|
131 | 145 | if let Some(repo_path_bytes) = &early_args.repo { |
@@ -145,6 +159,11 b' fn main() {' | |||
|
145 | 159 | repo_path_bytes |
|
146 | 160 | ), |
|
147 | 161 | }), |
|
162 | // TODO: show a warning or combine with original error if | |
|
163 | // `get_bool` returns an error | |
|
164 | non_repo_config | |
|
165 | .get_bool(b"ui", b"detailed-exit-code") | |
|
166 | .unwrap_or(false), | |
|
148 | 167 | ) |
|
149 | 168 | } |
|
150 | 169 | } |
@@ -160,6 +179,11 b' fn main() {' | |||
|
160 | 179 | &ui, |
|
161 | 180 | OnUnsupported::from_config(&ui, &non_repo_config), |
|
162 | 181 | Err(error.into()), |
|
182 | // TODO: show a warning or combine with original error if | |
|
183 | // `get_bool` returns an error | |
|
184 | non_repo_config | |
|
185 | .get_bool(b"ui", b"detailed-exit-code") | |
|
186 | .unwrap_or(false), | |
|
163 | 187 | ), |
|
164 | 188 | }; |
|
165 | 189 | |
@@ -176,13 +200,35 b' fn main() {' | |||
|
176 | 200 | repo_result.as_ref(), |
|
177 | 201 | config, |
|
178 | 202 | ); |
|
179 | exit(&initial_current_dir, &ui, on_unsupported, result) | |
|
203 | exit( | |
|
204 | &initial_current_dir, | |
|
205 | &ui, | |
|
206 | on_unsupported, | |
|
207 | result, | |
|
208 | // TODO: show a warning or combine with original error if `get_bool` | |
|
209 | // returns an error | |
|
210 | config | |
|
211 | .get_bool(b"ui", b"detailed-exit-code") | |
|
212 | .unwrap_or(false), | |
|
213 | ) | |
|
180 | 214 | } |
|
181 | 215 | |
|
182 | fn exit_code(result: &Result<(), CommandError>) -> i32 { | |
|
216 | fn exit_code( | |
|
217 | result: &Result<(), CommandError>, | |
|
218 | use_detailed_exit_code: bool, | |
|
219 | ) -> i32 { | |
|
183 | 220 | match result { |
|
184 | 221 | Ok(()) => exitcode::OK, |
|
185 |
Err(CommandError::Abort { |
|
|
222 | Err(CommandError::Abort { | |
|
223 | message: _, | |
|
224 | detailed_exit_code, | |
|
225 | }) => { | |
|
226 | if use_detailed_exit_code { | |
|
227 | *detailed_exit_code | |
|
228 | } else { | |
|
229 | exitcode::ABORT | |
|
230 | } | |
|
231 | } | |
|
186 | 232 | Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL, |
|
187 | 233 | |
|
188 | 234 | // Exit with a specific code and no error message to let a potential |
@@ -198,6 +244,7 b' fn exit(' | |||
|
198 | 244 | ui: &Ui, |
|
199 | 245 | mut on_unsupported: OnUnsupported, |
|
200 | 246 | result: Result<(), CommandError>, |
|
247 | use_detailed_exit_code: bool, | |
|
201 | 248 | ) -> ! { |
|
202 | 249 | if let ( |
|
203 | 250 | OnUnsupported::Fallback { executable }, |
@@ -238,18 +285,22 b' fn exit(' | |||
|
238 | 285 | } |
|
239 | 286 | } |
|
240 | 287 | } |
|
241 | exit_no_fallback(ui, on_unsupported, result) | |
|
288 | exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code) | |
|
242 | 289 | } |
|
243 | 290 | |
|
244 | 291 | fn exit_no_fallback( |
|
245 | 292 | ui: &Ui, |
|
246 | 293 | on_unsupported: OnUnsupported, |
|
247 | 294 | result: Result<(), CommandError>, |
|
295 | use_detailed_exit_code: bool, | |
|
248 | 296 | ) -> ! { |
|
249 | 297 | match &result { |
|
250 | 298 | Ok(_) => {} |
|
251 | 299 | Err(CommandError::Unsuccessful) => {} |
|
252 |
Err(CommandError::Abort { |
|
|
300 | Err(CommandError::Abort { | |
|
301 | message, | |
|
302 | detailed_exit_code: _, | |
|
303 | }) => { | |
|
253 | 304 | if !message.is_empty() { |
|
254 | 305 | // Ignore errors when writing to stderr, we’re already exiting |
|
255 | 306 | // with failure code so there’s not much more we can do. |
@@ -269,7 +320,7 b' fn exit_no_fallback(' | |||
|
269 | 320 | } |
|
270 | 321 | } |
|
271 | 322 | } |
|
272 | std::process::exit(exit_code(&result)) | |
|
323 | std::process::exit(exit_code(&result, use_detailed_exit_code)) | |
|
273 | 324 | } |
|
274 | 325 | |
|
275 | 326 | macro_rules! subcommands { |
@@ -411,6 +462,7 b' impl OnUnsupported {' | |||
|
411 | 462 | "abort: 'rhg.on-unsupported=fallback' without \ |
|
412 | 463 | 'rhg.fallback-executable' set." |
|
413 | 464 | )), |
|
465 | false, | |
|
414 | 466 | ) |
|
415 | 467 | }) |
|
416 | 468 | .to_owned(), |
@@ -3,8 +3,6 b' hide outer repo' | |||
|
3 | 3 | |
|
4 | 4 | Invalid syntax: no value |
|
5 | 5 | |
|
6 | TODO: add rhg support for detailed exit codes | |
|
7 | #if no-rhg | |
|
8 | 6 | $ cat > .hg/hgrc << EOF |
|
9 | 7 | > novaluekey |
|
10 | 8 | > EOF |
@@ -37,7 +35,6 b' Test hint about invalid syntax from lead' | |||
|
37 | 35 | $ hg showconfig |
|
38 | 36 | config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: [section] |
|
39 | 37 | [30] |
|
40 | #endif | |
|
41 | 38 |
|
|
42 | 39 | Reset hgrc |
|
43 | 40 |
@@ -90,12 +90,9 b" However, we can't prevent it from loadin" | |||
|
90 | 90 | |
|
91 | 91 | $ mkdir -p badrepo/.hg |
|
92 | 92 | $ echo 'invalid-syntax' > badrepo/.hg/hgrc |
|
93 | TODO: add rhg support for detailed exit codes | |
|
94 | #if no-rhg | |
|
95 | 93 | $ hg log -b -Rbadrepo default |
|
96 | 94 | config error at badrepo/.hg/hgrc:1: invalid-syntax |
|
97 | 95 | [30] |
|
98 | #endif | |
|
99 | 96 |
|
|
100 | 97 | $ hg log -b --cwd=inexistent default |
|
101 | 98 | abort: $ENOENT$: 'inexistent' |
General Comments 0
You need to be logged in to leave comments.
Login now