Show More
@@ -1,3 +1,4 b'' | |||||
|
1 | use crate::exitcode; | |||
1 | use crate::ui::utf8_to_local; |
|
2 | use crate::ui::utf8_to_local; | |
2 | use crate::ui::UiError; |
|
3 | use crate::ui::UiError; | |
3 | use crate::NoRepoInCwdError; |
|
4 | use crate::NoRepoInCwdError; | |
@@ -14,7 +15,10 b' use std::convert::From;' | |||||
14 | #[derive(Debug)] |
|
15 | #[derive(Debug)] | |
15 | pub enum CommandError { |
|
16 | pub enum CommandError { | |
16 | /// Exit with an error message and "standard" failure exit code. |
|
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 | /// Exit with a failure exit code but no message. |
|
23 | /// Exit with a failure exit code but no message. | |
20 | Unsuccessful, |
|
24 | Unsuccessful, | |
@@ -28,11 +32,19 b' pub enum CommandError {' | |||||
28 |
|
32 | |||
29 | impl CommandError { |
|
33 | impl CommandError { | |
30 | pub fn abort(message: impl AsRef<str>) -> Self { |
|
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 | CommandError::Abort { |
|
42 | CommandError::Abort { | |
32 | // TODO: bytes-based (instead of Unicode-based) formatting |
|
43 | // TODO: bytes-based (instead of Unicode-based) formatting | |
33 | // of error messages to handle non-UTF-8 filenames etc: |
|
44 | // of error messages to handle non-UTF-8 filenames etc: | |
34 | // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output |
|
45 | // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output | |
35 | message: utf8_to_local(message.as_ref()).into(), |
|
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 | impl From<ConfigValueParseError> for CommandError { |
|
77 | impl From<ConfigValueParseError> for CommandError { | |
66 | fn from(error: ConfigValueParseError) -> Self { |
|
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 | b"abort: repository {} not found", |
|
100 | b"abort: repository {} not found", | |
86 | get_bytes_from_path(at) |
|
101 | get_bytes_from_path(at) | |
87 | ), |
|
102 | ), | |
|
103 | detailed_exit_code: exitcode::ABORT, | |||
88 | }, |
|
104 | }, | |
89 | RepoError::ConfigParseError(error) => error.into(), |
|
105 | RepoError::ConfigParseError(error) => error.into(), | |
90 | RepoError::Other(error) => error.into(), |
|
106 | RepoError::Other(error) => error.into(), | |
@@ -100,6 +116,7 b" impl<'a> From<&'a NoRepoInCwdError> for " | |||||
100 | b"abort: no repository found in '{}' (.hg not found)!", |
|
116 | b"abort: no repository found in '{}' (.hg not found)!", | |
101 | get_bytes_from_path(cwd) |
|
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 | line_message, |
|
149 | line_message, | |
133 | message |
|
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 | /// Generic abort |
|
6 | /// Generic abort | |
7 | pub const ABORT: ExitCode = 255; |
|
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 | /// Generic something completed but did not succeed |
|
12 | /// Generic something completed but did not succeed | |
10 | pub const UNSUCCESSFUL: ExitCode = 1; |
|
13 | pub const UNSUCCESSFUL: ExitCode = 1; | |
11 |
|
14 |
@@ -82,7 +82,14 b' fn main_with_result(' | |||||
82 | let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?; |
|
82 | let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?; | |
83 | blackbox.log_command_start(); |
|
83 | blackbox.log_command_start(); | |
84 | let result = run(&invocation); |
|
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 | result |
|
93 | result | |
87 | } |
|
94 | } | |
88 |
|
95 | |||
@@ -114,6 +121,7 b' fn main() {' | |||||
114 | error, |
|
121 | error, | |
115 | cwd.display() |
|
122 | cwd.display() | |
116 | ))), |
|
123 | ))), | |
|
124 | false, | |||
117 | ) |
|
125 | ) | |
118 | }) |
|
126 | }) | |
119 | }); |
|
127 | }); | |
@@ -125,7 +133,13 b' fn main() {' | |||||
125 | // "unsupported" error but that is not enforced by the type system. |
|
133 | // "unsupported" error but that is not enforced by the type system. | |
126 | let on_unsupported = OnUnsupported::Abort; |
|
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 | if let Some(repo_path_bytes) = &early_args.repo { |
|
145 | if let Some(repo_path_bytes) = &early_args.repo { | |
@@ -145,6 +159,11 b' fn main() {' | |||||
145 | repo_path_bytes |
|
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 | &ui, |
|
179 | &ui, | |
161 | OnUnsupported::from_config(&ui, &non_repo_config), |
|
180 | OnUnsupported::from_config(&ui, &non_repo_config), | |
162 | Err(error.into()), |
|
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 | repo_result.as_ref(), |
|
200 | repo_result.as_ref(), | |
177 | config, |
|
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 | match result { |
|
220 | match result { | |
184 | Ok(()) => exitcode::OK, |
|
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 | Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL, |
|
232 | Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL, | |
187 |
|
233 | |||
188 | // Exit with a specific code and no error message to let a potential |
|
234 | // Exit with a specific code and no error message to let a potential | |
@@ -198,6 +244,7 b' fn exit(' | |||||
198 | ui: &Ui, |
|
244 | ui: &Ui, | |
199 | mut on_unsupported: OnUnsupported, |
|
245 | mut on_unsupported: OnUnsupported, | |
200 | result: Result<(), CommandError>, |
|
246 | result: Result<(), CommandError>, | |
|
247 | use_detailed_exit_code: bool, | |||
201 | ) -> ! { |
|
248 | ) -> ! { | |
202 | if let ( |
|
249 | if let ( | |
203 | OnUnsupported::Fallback { executable }, |
|
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 | fn exit_no_fallback( |
|
291 | fn exit_no_fallback( | |
245 | ui: &Ui, |
|
292 | ui: &Ui, | |
246 | on_unsupported: OnUnsupported, |
|
293 | on_unsupported: OnUnsupported, | |
247 | result: Result<(), CommandError>, |
|
294 | result: Result<(), CommandError>, | |
|
295 | use_detailed_exit_code: bool, | |||
248 | ) -> ! { |
|
296 | ) -> ! { | |
249 | match &result { |
|
297 | match &result { | |
250 | Ok(_) => {} |
|
298 | Ok(_) => {} | |
251 | Err(CommandError::Unsuccessful) => {} |
|
299 | Err(CommandError::Unsuccessful) => {} | |
252 |
Err(CommandError::Abort { |
|
300 | Err(CommandError::Abort { | |
|
301 | message, | |||
|
302 | detailed_exit_code: _, | |||
|
303 | }) => { | |||
253 | if !message.is_empty() { |
|
304 | if !message.is_empty() { | |
254 | // Ignore errors when writing to stderr, we’re already exiting |
|
305 | // Ignore errors when writing to stderr, we’re already exiting | |
255 | // with failure code so there’s not much more we can do. |
|
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 | macro_rules! subcommands { |
|
326 | macro_rules! subcommands { | |
@@ -411,6 +462,7 b' impl OnUnsupported {' | |||||
411 | "abort: 'rhg.on-unsupported=fallback' without \ |
|
462 | "abort: 'rhg.on-unsupported=fallback' without \ | |
412 | 'rhg.fallback-executable' set." |
|
463 | 'rhg.fallback-executable' set." | |
413 | )), |
|
464 | )), | |
|
465 | false, | |||
414 | ) |
|
466 | ) | |
415 | }) |
|
467 | }) | |
416 | .to_owned(), |
|
468 | .to_owned(), |
@@ -3,8 +3,6 b' hide outer repo' | |||||
3 |
|
3 | |||
4 | Invalid syntax: no value |
|
4 | Invalid syntax: no value | |
5 |
|
5 | |||
6 | TODO: add rhg support for detailed exit codes |
|
|||
7 | #if no-rhg |
|
|||
8 | $ cat > .hg/hgrc << EOF |
|
6 | $ cat > .hg/hgrc << EOF | |
9 | > novaluekey |
|
7 | > novaluekey | |
10 | > EOF |
|
8 | > EOF | |
@@ -37,7 +35,6 b' Test hint about invalid syntax from lead' | |||||
37 | $ hg showconfig |
|
35 | $ hg showconfig | |
38 | config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: [section] |
|
36 | config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: [section] | |
39 | [30] |
|
37 | [30] | |
40 | #endif |
|
|||
41 |
|
|
38 | ||
42 | Reset hgrc |
|
39 | Reset hgrc | |
43 |
|
40 |
@@ -90,12 +90,9 b" However, we can't prevent it from loadin" | |||||
90 |
|
90 | |||
91 | $ mkdir -p badrepo/.hg |
|
91 | $ mkdir -p badrepo/.hg | |
92 | $ echo 'invalid-syntax' > badrepo/.hg/hgrc |
|
92 | $ echo 'invalid-syntax' > badrepo/.hg/hgrc | |
93 | TODO: add rhg support for detailed exit codes |
|
|||
94 | #if no-rhg |
|
|||
95 | $ hg log -b -Rbadrepo default |
|
93 | $ hg log -b -Rbadrepo default | |
96 | config error at badrepo/.hg/hgrc:1: invalid-syntax |
|
94 | config error at badrepo/.hg/hgrc:1: invalid-syntax | |
97 | [30] |
|
95 | [30] | |
98 | #endif |
|
|||
99 |
|
|
96 | ||
100 | $ hg log -b --cwd=inexistent default |
|
97 | $ hg log -b --cwd=inexistent default | |
101 | abort: $ENOENT$: 'inexistent' |
|
98 | abort: $ENOENT$: 'inexistent' |
General Comments 0
You need to be logged in to leave comments.
Login now