Show More
@@ -0,0 +1,19 b'' | |||||
|
1 | pub type ExitCode = i32; | |||
|
2 | ||||
|
3 | /// Successful exit | |||
|
4 | pub const OK: ExitCode = 0; | |||
|
5 | ||||
|
6 | /// Generic abort | |||
|
7 | pub const ABORT: ExitCode = 255; | |||
|
8 | ||||
|
9 | // Abort when there is a config related error | |||
|
10 | pub const CONFIG_ERROR_ABORT: ExitCode = 30; | |||
|
11 | ||||
|
12 | // Abort when there is an error while parsing config | |||
|
13 | pub const CONFIG_PARSE_ERROR_ABORT: ExitCode = 10; | |||
|
14 | ||||
|
15 | /// Generic something completed but did not succeed | |||
|
16 | pub const UNSUCCESSFUL: ExitCode = 1; | |||
|
17 | ||||
|
18 | /// Command or feature not implemented by rhg | |||
|
19 | pub const UNIMPLEMENTED: ExitCode = 252; |
@@ -8,6 +8,7 b'' | |||||
8 | // GNU General Public License version 2 or any later version. |
|
8 | // GNU General Public License version 2 or any later version. | |
9 |
|
9 | |||
10 | use crate::errors::HgError; |
|
10 | use crate::errors::HgError; | |
|
11 | use crate::exit_codes::CONFIG_PARSE_ERROR_ABORT; | |||
11 | use crate::utils::files::{get_bytes_from_path, get_path_from_bytes}; |
|
12 | use crate::utils::files::{get_bytes_from_path, get_path_from_bytes}; | |
12 | use format_bytes::{format_bytes, write_bytes, DisplayBytes}; |
|
13 | use format_bytes::{format_bytes, write_bytes, DisplayBytes}; | |
13 | use lazy_static::lazy_static; |
|
14 | use lazy_static::lazy_static; | |
@@ -73,11 +74,14 b' impl ConfigLayer {' | |||||
73 | if let Some((section, item, value)) = parse_one(arg) { |
|
74 | if let Some((section, item, value)) = parse_one(arg) { | |
74 | layer.add(section, item, value, None); |
|
75 | layer.add(section, item, value, None); | |
75 | } else { |
|
76 | } else { | |
76 |
Err(HgError::abort( |
|
77 | Err(HgError::abort( | |
77 | "abort: malformed --config option: '{}' \ |
|
78 | format!( | |
|
79 | "abort: malformed --config option: '{}' \ | |||
78 | (use --config section.name=value)", |
|
80 | (use --config section.name=value)", | |
79 | String::from_utf8_lossy(arg), |
|
81 | String::from_utf8_lossy(arg), | |
80 |
) |
|
82 | ), | |
|
83 | CONFIG_PARSE_ERROR_ABORT, | |||
|
84 | ))? | |||
81 | } |
|
85 | } | |
82 | } |
|
86 | } | |
83 | if layer.sections.is_empty() { |
|
87 | if layer.sections.is_empty() { |
@@ -1,4 +1,5 b'' | |||||
1 | use crate::config::ConfigValueParseError; |
|
1 | use crate::config::ConfigValueParseError; | |
|
2 | use crate::exit_codes; | |||
2 | use std::fmt; |
|
3 | use std::fmt; | |
3 |
|
4 | |||
4 | /// Common error cases that can happen in many different APIs |
|
5 | /// Common error cases that can happen in many different APIs | |
@@ -27,9 +28,12 b' pub enum HgError {' | |||||
27 |
|
28 | |||
28 | /// Operation cannot proceed for some other reason. |
|
29 | /// Operation cannot proceed for some other reason. | |
29 | /// |
|
30 | /// | |
30 |
/// The |
|
31 | /// The message is a short explanation for users, not intended to be | |
31 | /// machine-readable. |
|
32 | /// machine-readable. | |
32 |
Abort |
|
33 | Abort { | |
|
34 | message: String, | |||
|
35 | detailed_exit_code: exit_codes::ExitCode, | |||
|
36 | }, | |||
33 |
|
37 | |||
34 | /// A configuration value is not in the expected syntax. |
|
38 | /// A configuration value is not in the expected syntax. | |
35 | /// |
|
39 | /// | |
@@ -69,8 +73,15 b' impl HgError {' | |||||
69 | pub fn unsupported(explanation: impl Into<String>) -> Self { |
|
73 | pub fn unsupported(explanation: impl Into<String>) -> Self { | |
70 | HgError::UnsupportedFeature(explanation.into()) |
|
74 | HgError::UnsupportedFeature(explanation.into()) | |
71 | } |
|
75 | } | |
72 | pub fn abort(explanation: impl Into<String>) -> Self { |
|
76 | ||
73 | HgError::Abort(explanation.into()) |
|
77 | pub fn abort( | |
|
78 | explanation: impl Into<String>, | |||
|
79 | exit_code: exit_codes::ExitCode, | |||
|
80 | ) -> Self { | |||
|
81 | HgError::Abort { | |||
|
82 | message: explanation.into(), | |||
|
83 | detailed_exit_code: exit_code, | |||
|
84 | } | |||
74 | } |
|
85 | } | |
75 | } |
|
86 | } | |
76 |
|
87 | |||
@@ -78,7 +89,7 b' impl HgError {' | |||||
78 | impl fmt::Display for HgError { |
|
89 | impl fmt::Display for HgError { | |
79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
80 | match self { |
|
91 | match self { | |
81 |
HgError::Abort |
|
92 | HgError::Abort { message, .. } => write!(f, "{}", message), | |
82 | HgError::IoError { error, context } => { |
|
93 | HgError::IoError { error, context } => { | |
83 | write!(f, "abort: {}: {}", context, error) |
|
94 | write!(f, "abort: {}: {}", context, error) | |
84 | } |
|
95 | } |
@@ -11,6 +11,7 b' pub use ancestors::{AncestorsIterator, L' | |||||
11 | pub mod dirstate; |
|
11 | pub mod dirstate; | |
12 | pub mod dirstate_tree; |
|
12 | pub mod dirstate_tree; | |
13 | pub mod discovery; |
|
13 | pub mod discovery; | |
|
14 | pub mod exit_codes; | |||
14 | pub mod requirements; |
|
15 | pub mod requirements; | |
15 | pub mod testing; // unconditionally built, for use from integration tests |
|
16 | pub mod testing; // unconditionally built, for use from integration tests | |
16 | pub use dirstate::{ |
|
17 | pub use dirstate::{ |
@@ -1,5 +1,6 b'' | |||||
1 | use crate::config::{Config, ConfigError, ConfigParseError}; |
|
1 | use crate::config::{Config, ConfigError, ConfigParseError}; | |
2 | use crate::errors::{HgError, IoErrorContext, IoResultExt}; |
|
2 | use crate::errors::{HgError, IoErrorContext, IoResultExt}; | |
|
3 | use crate::exit_codes; | |||
3 | use crate::requirements; |
|
4 | use crate::requirements; | |
4 | use crate::utils::files::get_path_from_bytes; |
|
5 | use crate::utils::files::get_path_from_bytes; | |
5 | use crate::utils::SliceExt; |
|
6 | use crate::utils::SliceExt; | |
@@ -150,6 +151,7 b' impl Repo {' | |||||
150 | Some(b"abort") | None => HgError::abort( |
|
151 | Some(b"abort") | None => HgError::abort( | |
151 | "abort: share source does not support share-safe requirement\n\ |
|
152 | "abort: share source does not support share-safe requirement\n\ | |
152 | (see `hg help config.format.use-share-safe` for more information)", |
|
153 | (see `hg help config.format.use-share-safe` for more information)", | |
|
154 | exit_codes::ABORT, | |||
153 | ), |
|
155 | ), | |
154 | _ => HgError::unsupported("share-safe downgrade"), |
|
156 | _ => HgError::unsupported("share-safe downgrade"), | |
155 | } |
|
157 | } | |
@@ -161,6 +163,7 b' impl Repo {' | |||||
161 | "abort: version mismatch: source uses share-safe \ |
|
163 | "abort: version mismatch: source uses share-safe \ | |
162 | functionality while the current share does not\n\ |
|
164 | functionality while the current share does not\n\ | |
163 | (see `hg help config.format.use-share-safe` for more information)", |
|
165 | (see `hg help config.format.use-share-safe` for more information)", | |
|
166 | exit_codes::ABORT, | |||
164 | ), |
|
167 | ), | |
165 | _ => HgError::unsupported("share-safe upgrade"), |
|
168 | _ => HgError::unsupported("share-safe upgrade"), | |
166 | } |
|
169 | } |
@@ -1,10 +1,10 b'' | |||||
1 | use crate::exitcode; |
|
|||
2 |
|
|
1 | use crate::ui::utf8_to_local; | |
3 | use crate::ui::UiError; |
|
2 | use crate::ui::UiError; | |
4 | use crate::NoRepoInCwdError; |
|
3 | use crate::NoRepoInCwdError; | |
5 | use format_bytes::format_bytes; |
|
4 | use format_bytes::format_bytes; | |
6 | use hg::config::{ConfigError, ConfigParseError, ConfigValueParseError}; |
|
5 | use hg::config::{ConfigError, ConfigParseError, ConfigValueParseError}; | |
7 | use hg::errors::HgError; |
|
6 | use hg::errors::HgError; | |
|
7 | use hg::exit_codes; | |||
8 | use hg::repo::RepoError; |
|
8 | use hg::repo::RepoError; | |
9 | use hg::revlog::revlog::RevlogError; |
|
9 | use hg::revlog::revlog::RevlogError; | |
10 | use hg::utils::files::get_bytes_from_path; |
|
10 | use hg::utils::files::get_bytes_from_path; | |
@@ -17,7 +17,7 b' pub enum CommandError {' | |||||
17 | /// Exit with an error message and "standard" failure exit code. |
|
17 | /// Exit with an error message and "standard" failure exit code. | |
18 | Abort { |
|
18 | Abort { | |
19 | message: Vec<u8>, |
|
19 | message: Vec<u8>, | |
20 | detailed_exit_code: exitcode::ExitCode, |
|
20 | detailed_exit_code: exit_codes::ExitCode, | |
21 | }, |
|
21 | }, | |
22 |
|
22 | |||
23 | /// Exit with a failure exit code but no message. |
|
23 | /// Exit with a failure exit code but no message. | |
@@ -32,12 +32,12 b' pub enum CommandError {' | |||||
32 |
|
32 | |||
33 | impl CommandError { |
|
33 | impl CommandError { | |
34 | 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) |
|
35 | CommandError::abort_with_exit_code(message, exit_codes::ABORT) | |
36 | } |
|
36 | } | |
37 |
|
37 | |||
38 | pub fn abort_with_exit_code( |
|
38 | pub fn abort_with_exit_code( | |
39 | message: impl AsRef<str>, |
|
39 | message: impl AsRef<str>, | |
40 | detailed_exit_code: exitcode::ExitCode, |
|
40 | detailed_exit_code: exit_codes::ExitCode, | |
41 | ) -> Self { |
|
41 | ) -> Self { | |
42 | CommandError::Abort { |
|
42 | CommandError::Abort { | |
43 | // TODO: bytes-based (instead of Unicode-based) formatting |
|
43 | // TODO: bytes-based (instead of Unicode-based) formatting | |
@@ -78,7 +78,7 b' impl From<ConfigValueParseError> for Com' | |||||
78 | fn from(error: ConfigValueParseError) -> Self { |
|
78 | fn from(error: ConfigValueParseError) -> Self { | |
79 | CommandError::abort_with_exit_code( |
|
79 | CommandError::abort_with_exit_code( | |
80 | error.to_string(), |
|
80 | error.to_string(), | |
81 | exitcode::CONFIG_ERROR_ABORT, |
|
81 | exit_codes::CONFIG_ERROR_ABORT, | |
82 | ) |
|
82 | ) | |
83 | } |
|
83 | } | |
84 | } |
|
84 | } | |
@@ -100,7 +100,7 b' impl From<RepoError> for CommandError {' | |||||
100 | b"abort: repository {} not found", |
|
100 | b"abort: repository {} not found", | |
101 | get_bytes_from_path(at) |
|
101 | get_bytes_from_path(at) | |
102 | ), |
|
102 | ), | |
103 | detailed_exit_code: exitcode::ABORT, |
|
103 | detailed_exit_code: exit_codes::ABORT, | |
104 | }, |
|
104 | }, | |
105 | RepoError::ConfigParseError(error) => error.into(), |
|
105 | RepoError::ConfigParseError(error) => error.into(), | |
106 | RepoError::Other(error) => error.into(), |
|
106 | RepoError::Other(error) => error.into(), | |
@@ -116,7 +116,7 b" impl<'a> From<&'a NoRepoInCwdError> for " | |||||
116 | b"abort: no repository found in '{}' (.hg not found)!", |
|
116 | b"abort: no repository found in '{}' (.hg not found)!", | |
117 | get_bytes_from_path(cwd) |
|
117 | get_bytes_from_path(cwd) | |
118 | ), |
|
118 | ), | |
119 | detailed_exit_code: exitcode::ABORT, |
|
119 | detailed_exit_code: exit_codes::ABORT, | |
120 | } |
|
120 | } | |
121 | } |
|
121 | } | |
122 | } |
|
122 | } | |
@@ -149,7 +149,7 b' impl From<ConfigParseError> for CommandE' | |||||
149 | line_message, |
|
149 | line_message, | |
150 | message |
|
150 | message | |
151 | ), |
|
151 | ), | |
152 | detailed_exit_code: exitcode::CONFIG_ERROR_ABORT, |
|
152 | detailed_exit_code: exit_codes::CONFIG_ERROR_ABORT, | |
153 | } |
|
153 | } | |
154 | } |
|
154 | } | |
155 | } |
|
155 | } |
@@ -6,6 +6,7 b' use clap::Arg;' | |||||
6 | use clap::ArgMatches; |
|
6 | use clap::ArgMatches; | |
7 | use format_bytes::{format_bytes, join}; |
|
7 | use format_bytes::{format_bytes, join}; | |
8 | use hg::config::{Config, ConfigSource}; |
|
8 | use hg::config::{Config, ConfigSource}; | |
|
9 | use hg::exit_codes; | |||
9 | use hg::repo::{Repo, RepoError}; |
|
10 | use hg::repo::{Repo, RepoError}; | |
10 | use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes}; |
|
11 | use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes}; | |
11 | use hg::utils::SliceExt; |
|
12 | use hg::utils::SliceExt; | |
@@ -15,7 +16,6 b' use std::process::Command;' | |||||
15 |
|
16 | |||
16 | mod blackbox; |
|
17 | mod blackbox; | |
17 | mod error; |
|
18 | mod error; | |
18 | mod exitcode; |
|
|||
19 | mod ui; |
|
19 | mod ui; | |
20 | use error::CommandError; |
|
20 | use error::CommandError; | |
21 |
|
21 | |||
@@ -297,7 +297,7 b' fn exit_code(' | |||||
297 | use_detailed_exit_code: bool, |
|
297 | use_detailed_exit_code: bool, | |
298 | ) -> i32 { |
|
298 | ) -> i32 { | |
299 | match result { |
|
299 | match result { | |
300 | Ok(()) => exitcode::OK, |
|
300 | Ok(()) => exit_codes::OK, | |
301 | Err(CommandError::Abort { |
|
301 | Err(CommandError::Abort { | |
302 | message: _, |
|
302 | message: _, | |
303 | detailed_exit_code, |
|
303 | detailed_exit_code, | |
@@ -305,15 +305,15 b' fn exit_code(' | |||||
305 | if use_detailed_exit_code { |
|
305 | if use_detailed_exit_code { | |
306 | *detailed_exit_code |
|
306 | *detailed_exit_code | |
307 | } else { |
|
307 | } else { | |
308 | exitcode::ABORT |
|
308 | exit_codes::ABORT | |
309 | } |
|
309 | } | |
310 | } |
|
310 | } | |
311 | Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL, |
|
311 | Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL, | |
312 |
|
312 | |||
313 | // Exit with a specific code and no error message to let a potential |
|
313 | // Exit with a specific code and no error message to let a potential | |
314 | // wrapper script fallback to Python-based Mercurial. |
|
314 | // wrapper script fallback to Python-based Mercurial. | |
315 | Err(CommandError::UnsupportedFeature { .. }) => { |
|
315 | Err(CommandError::UnsupportedFeature { .. }) => { | |
316 | exitcode::UNIMPLEMENTED |
|
316 | exit_codes::UNIMPLEMENTED | |
317 | } |
|
317 | } | |
318 | } |
|
318 | } | |
319 | } |
|
319 | } | |
@@ -352,7 +352,7 b' fn exit(' | |||||
352 | let result = command.status(); |
|
352 | let result = command.status(); | |
353 | match result { |
|
353 | match result { | |
354 | Ok(status) => std::process::exit( |
|
354 | Ok(status) => std::process::exit( | |
355 | status.code().unwrap_or(exitcode::ABORT), |
|
355 | status.code().unwrap_or(exit_codes::ABORT), | |
356 | ), |
|
356 | ), | |
357 | Err(error) => { |
|
357 | Err(error) => { | |
358 | let _ = ui.write_stderr(&format_bytes!( |
|
358 | let _ = ui.write_stderr(&format_bytes!( |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now