error.rs
211 lines
| 6.5 KiB
| application/rls-services+xml
|
RustLexer
Simon Sapin
|
r47165 | use crate::ui::utf8_to_local; | ||
Antoine Cezar
|
r45592 | use crate::ui::UiError; | ||
Simon Sapin
|
r47335 | use crate::NoRepoInCwdError; | ||
Simon Sapin
|
r47175 | use format_bytes::format_bytes; | ||
Simon Sapin
|
r47555 | use hg::config::{ConfigError, ConfigParseError, ConfigValueParseError}; | ||
Simon Sapin
|
r48474 | use hg::dirstate_tree::on_disk::DirstateV2ParseError; | ||
Simon Sapin
|
r47175 | use hg::errors::HgError; | ||
Pulkit Goyal
|
r48199 | use hg::exit_codes; | ||
Simon Sapin
|
r47215 | use hg::repo::RepoError; | ||
Simon Sapin
|
r47166 | use hg::revlog::revlog::RevlogError; | ||
Simon Sapin
|
r47175 | use hg::utils::files::get_bytes_from_path; | ||
Simon Sapin
|
r47555 | use hg::{DirstateError, DirstateMapError, StatusError}; | ||
Antoine Cezar
|
r45592 | use std::convert::From; | ||
/// The kind of command error | ||||
Simon Sapin
|
r47174 | #[derive(Debug)] | ||
Simon Sapin
|
r47163 | pub enum CommandError { | ||
Simon Sapin
|
r47174 | /// Exit with an error message and "standard" failure exit code. | ||
Pulkit Goyal
|
r47576 | Abort { | ||
message: Vec<u8>, | ||||
Pulkit Goyal
|
r48199 | detailed_exit_code: exit_codes::ExitCode, | ||
Pulkit Goyal
|
r47576 | }, | ||
Simon Sapin
|
r47174 | |||
Simon Sapin
|
r47478 | /// Exit with a failure exit code but no message. | ||
Unsuccessful, | ||||
Simon Sapin
|
r47424 | /// Encountered something (such as a CLI argument, repository layout, …) | ||
/// not supported by this version of `rhg`. Depending on configuration | ||||
/// `rhg` may attempt to silently fall back to Python-based `hg`, which | ||||
/// may or may not support this feature. | ||||
UnsupportedFeature { message: Vec<u8> }, | ||||
Raphaël Gomès
|
r50043 | /// The fallback executable does not exist (or has some other problem if | ||
/// we end up being more precise about broken fallbacks). | ||||
InvalidFallback { path: Vec<u8>, err: String }, | ||||
Antoine Cezar
|
r45592 | } | ||
Simon Sapin
|
r47163 | impl CommandError { | ||
Simon Sapin
|
r47174 | pub fn abort(message: impl AsRef<str>) -> Self { | ||
Pulkit Goyal
|
r48199 | CommandError::abort_with_exit_code(message, exit_codes::ABORT) | ||
Pulkit Goyal
|
r47576 | } | ||
pub fn abort_with_exit_code( | ||||
message: impl AsRef<str>, | ||||
Pulkit Goyal
|
r48199 | detailed_exit_code: exit_codes::ExitCode, | ||
Pulkit Goyal
|
r47576 | ) -> Self { | ||
Simon Sapin
|
r47174 | CommandError::Abort { | ||
// TODO: bytes-based (instead of Unicode-based) formatting | ||||
// of error messages to handle non-UTF-8 filenames etc: | ||||
// https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output | ||||
message: utf8_to_local(message.as_ref()).into(), | ||||
Pulkit Goyal
|
r47576 | detailed_exit_code: detailed_exit_code, | ||
Antoine Cezar
|
r45592 | } | ||
} | ||||
Simon Sapin
|
r47424 | |||
pub fn unsupported(message: impl AsRef<str>) -> Self { | ||||
CommandError::UnsupportedFeature { | ||||
message: utf8_to_local(message.as_ref()).into(), | ||||
} | ||||
} | ||||
Simon Sapin
|
r47174 | } | ||
Antoine Cezar
|
r45920 | |||
Simon Sapin
|
r47333 | /// For now we don’t differenciate between invalid CLI args and valid for `hg` | ||
/// but not supported yet by `rhg`. | ||||
impl From<clap::Error> for CommandError { | ||||
Simon Sapin
|
r47424 | fn from(error: clap::Error) -> Self { | ||
CommandError::unsupported(error.to_string()) | ||||
Simon Sapin
|
r47333 | } | ||
} | ||||
Simon Sapin
|
r47174 | impl From<HgError> for CommandError { | ||
fn from(error: HgError) -> Self { | ||||
match error { | ||||
Simon Sapin
|
r47424 | HgError::UnsupportedFeature(message) => { | ||
CommandError::unsupported(message) | ||||
} | ||||
Pulkit Goyal
|
r48200 | HgError::Abort { | ||
message, | ||||
detailed_exit_code, | ||||
} => { | ||||
CommandError::abort_with_exit_code(message, detailed_exit_code) | ||||
} | ||||
Simon Sapin
|
r47174 | _ => CommandError::abort(error.to_string()), | ||
Antoine Cezar
|
r45920 | } | ||
} | ||||
Antoine Cezar
|
r45592 | } | ||
Simon Sapin
|
r47555 | impl From<ConfigValueParseError> for CommandError { | ||
fn from(error: ConfigValueParseError) -> Self { | ||||
Pulkit Goyal
|
r47576 | CommandError::abort_with_exit_code( | ||
error.to_string(), | ||||
Pulkit Goyal
|
r48199 | exit_codes::CONFIG_ERROR_ABORT, | ||
Pulkit Goyal
|
r47576 | ) | ||
Simon Sapin
|
r47555 | } | ||
} | ||||
Antoine Cezar
|
r45592 | impl From<UiError> for CommandError { | ||
Simon Sapin
|
r47174 | fn from(_error: UiError) -> Self { | ||
// If we already failed writing to stdout or stderr, | ||||
// writing an error message to stderr about it would be likely to fail | ||||
// too. | ||||
CommandError::abort("") | ||||
Antoine Cezar
|
r45592 | } | ||
} | ||||
Antoine Cezar
|
r45922 | |||
Simon Sapin
|
r47215 | impl From<RepoError> for CommandError { | ||
fn from(error: RepoError) -> Self { | ||||
Simon Sapin
|
r47175 | match error { | ||
Simon Sapin
|
r47253 | RepoError::NotFound { at } => CommandError::Abort { | ||
Simon Sapin
|
r47175 | message: format_bytes!( | ||
Simon Sapin
|
r47469 | b"abort: repository {} not found", | ||
Simon Sapin
|
r47253 | get_bytes_from_path(at) | ||
Simon Sapin
|
r47175 | ), | ||
Pulkit Goyal
|
r48199 | detailed_exit_code: exit_codes::ABORT, | ||
Simon Sapin
|
r47175 | }, | ||
Simon Sapin
|
r47215 | RepoError::ConfigParseError(error) => error.into(), | ||
RepoError::Other(error) => error.into(), | ||||
Antoine Cezar
|
r45922 | } | ||
} | ||||
} | ||||
Simon Sapin
|
r47165 | |||
Simon Sapin
|
r47335 | impl<'a> From<&'a NoRepoInCwdError> for CommandError { | ||
fn from(error: &'a NoRepoInCwdError) -> Self { | ||||
let NoRepoInCwdError { cwd } = error; | ||||
CommandError::Abort { | ||||
message: format_bytes!( | ||||
Simon Sapin
|
r47465 | b"abort: no repository found in '{}' (.hg not found)!", | ||
Simon Sapin
|
r47335 | get_bytes_from_path(cwd) | ||
), | ||||
Pulkit Goyal
|
r48199 | detailed_exit_code: exit_codes::ABORT, | ||
Simon Sapin
|
r47335 | } | ||
} | ||||
} | ||||
Simon Sapin
|
r47213 | impl From<ConfigError> for CommandError { | ||
fn from(error: ConfigError) -> Self { | ||||
match error { | ||||
Simon Sapin
|
r47215 | ConfigError::Parse(error) => error.into(), | ||
Simon Sapin
|
r47213 | ConfigError::Other(error) => error.into(), | ||
} | ||||
} | ||||
} | ||||
Simon Sapin
|
r47215 | impl From<ConfigParseError> for CommandError { | ||
fn from(error: ConfigParseError) -> Self { | ||||
let ConfigParseError { | ||||
origin, | ||||
line, | ||||
Simon Sapin
|
r47465 | message, | ||
Simon Sapin
|
r47215 | } = error; | ||
let line_message = if let Some(line_number) = line { | ||||
Simon Sapin
|
r47465 | format_bytes!(b":{}", line_number.to_string().into_bytes()) | ||
Simon Sapin
|
r47215 | } else { | ||
Vec::new() | ||||
}; | ||||
CommandError::Abort { | ||||
message: format_bytes!( | ||||
Simon Sapin
|
r47465 | b"config error at {}{}: {}", | ||
Simon Sapin
|
r47249 | origin, | ||
Simon Sapin
|
r47215 | line_message, | ||
Simon Sapin
|
r47465 | message | ||
Simon Sapin
|
r47215 | ), | ||
Pulkit Goyal
|
r48199 | detailed_exit_code: exit_codes::CONFIG_ERROR_ABORT, | ||
Simon Sapin
|
r47215 | } | ||
} | ||||
} | ||||
Simon Sapin
|
r47166 | impl From<(RevlogError, &str)> for CommandError { | ||
fn from((err, rev): (RevlogError, &str)) -> CommandError { | ||||
Simon Sapin
|
r47165 | match err { | ||
Pulkit Goyal
|
r47577 | RevlogError::WDirUnsupported => CommandError::abort( | ||
"abort: working directory revision cannot be specified", | ||||
), | ||||
Simon Sapin
|
r47174 | RevlogError::InvalidRevision => CommandError::abort(format!( | ||
Simon Sapin
|
r47465 | "abort: invalid revision identifier: {}", | ||
Simon Sapin
|
r47174 | rev | ||
Simon Sapin
|
r47165 | )), | ||
Simon Sapin
|
r47174 | RevlogError::AmbiguousPrefix => CommandError::abort(format!( | ||
Simon Sapin
|
r47465 | "abort: ambiguous revision identifier: {}", | ||
Simon Sapin
|
r47174 | rev | ||
Simon Sapin
|
r47165 | )), | ||
Simon Sapin
|
r47174 | RevlogError::Other(error) => error.into(), | ||
Simon Sapin
|
r47165 | } | ||
} | ||||
} | ||||
Simon Sapin
|
r47555 | |||
impl From<StatusError> for CommandError { | ||||
fn from(error: StatusError) -> Self { | ||||
CommandError::abort(format!("{}", error)) | ||||
} | ||||
} | ||||
impl From<DirstateMapError> for CommandError { | ||||
fn from(error: DirstateMapError) -> Self { | ||||
CommandError::abort(format!("{}", error)) | ||||
} | ||||
} | ||||
impl From<DirstateError> for CommandError { | ||||
fn from(error: DirstateError) -> Self { | ||||
match error { | ||||
DirstateError::Common(error) => error.into(), | ||||
DirstateError::Map(error) => error.into(), | ||||
} | ||||
} | ||||
} | ||||
Simon Sapin
|
r48474 | |||
impl From<DirstateV2ParseError> for CommandError { | ||||
fn from(error: DirstateV2ParseError) -> Self { | ||||
HgError::from(error).into() | ||||
} | ||||
} | ||||