mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
cli: allow to attach multiple hints to CommandError
Even though the number of the hints is usually 0 or 1, this simplifies the API and hints handling.
This commit is contained in:
parent
bb0dcc059c
commit
2523dc188c
2 changed files with 33 additions and 37 deletions
|
@ -2591,9 +2591,6 @@ fn map_clap_cli_error(
|
||||||
ui: &Ui,
|
ui: &Ui,
|
||||||
layered_configs: &LayeredConfigs,
|
layered_configs: &LayeredConfigs,
|
||||||
) -> CommandError {
|
) -> CommandError {
|
||||||
if cmd_err.hint.is_some() {
|
|
||||||
return cmd_err;
|
|
||||||
}
|
|
||||||
let Some(err) = cmd_err.error.downcast_ref::<clap::Error>() else {
|
let Some(err) = cmd_err.error.downcast_ref::<clap::Error>() else {
|
||||||
return cmd_err;
|
return cmd_err;
|
||||||
};
|
};
|
||||||
|
@ -2604,7 +2601,7 @@ fn map_clap_cli_error(
|
||||||
if arg.as_str() == "--template <TEMPLATE>" && value.is_empty() {
|
if arg.as_str() == "--template <TEMPLATE>" && value.is_empty() {
|
||||||
// Suppress the error, it's less important than the original error.
|
// Suppress the error, it's less important than the original error.
|
||||||
if let Ok(template_aliases) = load_template_aliases(ui, layered_configs) {
|
if let Ok(template_aliases) = load_template_aliases(ui, layered_configs) {
|
||||||
cmd_err.hint = Some(format_template_aliases_hint(&template_aliases));
|
cmd_err.add_hint(format_template_aliases_hint(&template_aliases));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub enum CommandErrorKind {
|
||||||
pub struct CommandError {
|
pub struct CommandError {
|
||||||
pub kind: CommandErrorKind,
|
pub kind: CommandErrorKind,
|
||||||
pub error: Arc<dyn error::Error + Send + Sync>,
|
pub error: Arc<dyn error::Error + Send + Sync>,
|
||||||
pub hint: Option<String>,
|
pub hints: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandError {
|
impl CommandError {
|
||||||
|
@ -67,23 +67,25 @@ impl CommandError {
|
||||||
CommandError {
|
CommandError {
|
||||||
kind,
|
kind,
|
||||||
error: Arc::from(err.into()),
|
error: Arc::from(err.into()),
|
||||||
hint: None,
|
hints: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_hint_opt(
|
/// Returns error with the given `hint` attached.
|
||||||
kind: CommandErrorKind,
|
pub fn hinted(mut self, hint: impl Into<String>) -> Self {
|
||||||
err: impl Into<Box<dyn error::Error + Send + Sync>>,
|
self.add_hint(hint);
|
||||||
hint: Option<String>,
|
self
|
||||||
) -> Self {
|
|
||||||
CommandError {
|
|
||||||
kind,
|
|
||||||
error: Arc::from(err.into()),
|
|
||||||
hint,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add method to attach hint?
|
/// Appends `hint` to the error.
|
||||||
|
pub fn add_hint(&mut self, hint: impl Into<String>) {
|
||||||
|
self.hints.push(hint.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends 0 or more `hints` to the error.
|
||||||
|
pub fn extend_hints(&mut self, hints: impl IntoIterator<Item = String>) {
|
||||||
|
self.hints.extend(hints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps error with user-visible message.
|
/// Wraps error with user-visible message.
|
||||||
|
@ -107,21 +109,21 @@ impl ErrorWithMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_error(err: impl Into<Box<dyn error::Error + Send + Sync>>) -> CommandError {
|
pub fn user_error(err: impl Into<Box<dyn error::Error + Send + Sync>>) -> CommandError {
|
||||||
user_error_with_hint_opt(err, None)
|
CommandError::new(CommandErrorKind::User, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_error_with_hint(
|
pub fn user_error_with_hint(
|
||||||
err: impl Into<Box<dyn error::Error + Send + Sync>>,
|
err: impl Into<Box<dyn error::Error + Send + Sync>>,
|
||||||
hint: impl Into<String>,
|
hint: impl Into<String>,
|
||||||
) -> CommandError {
|
) -> CommandError {
|
||||||
user_error_with_hint_opt(err, Some(hint.into()))
|
user_error(err).hinted(hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_error_with_message(
|
pub fn user_error_with_message(
|
||||||
message: impl Into<String>,
|
message: impl Into<String>,
|
||||||
source: impl Into<Box<dyn error::Error + Send + Sync>>,
|
source: impl Into<Box<dyn error::Error + Send + Sync>>,
|
||||||
) -> CommandError {
|
) -> CommandError {
|
||||||
user_error_with_hint_opt(ErrorWithMessage::new(message, source), None)
|
user_error(ErrorWithMessage::new(message, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_error_with_message_and_hint(
|
pub fn user_error_with_message_and_hint(
|
||||||
|
@ -129,14 +131,16 @@ pub fn user_error_with_message_and_hint(
|
||||||
hint: impl Into<String>,
|
hint: impl Into<String>,
|
||||||
source: impl Into<Box<dyn error::Error + Send + Sync>>,
|
source: impl Into<Box<dyn error::Error + Send + Sync>>,
|
||||||
) -> CommandError {
|
) -> CommandError {
|
||||||
user_error_with_hint_opt(ErrorWithMessage::new(message, source), Some(hint.into()))
|
user_error_with_message(message, source).hinted(hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_error_with_hint_opt(
|
pub fn user_error_with_hint_opt(
|
||||||
err: impl Into<Box<dyn error::Error + Send + Sync>>,
|
err: impl Into<Box<dyn error::Error + Send + Sync>>,
|
||||||
hint: Option<String>,
|
hint: Option<String>,
|
||||||
) -> CommandError {
|
) -> CommandError {
|
||||||
CommandError::with_hint_opt(CommandErrorKind::User, err, hint)
|
let mut err = CommandError::new(CommandErrorKind::User, err);
|
||||||
|
err.extend_hints(hint);
|
||||||
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config_error(err: impl Into<Box<dyn error::Error + Send + Sync>>) -> CommandError {
|
pub fn config_error(err: impl Into<Box<dyn error::Error + Send + Sync>>) -> CommandError {
|
||||||
|
@ -504,14 +508,14 @@ fn try_handle_command_result(
|
||||||
return Ok(ExitCode::SUCCESS);
|
return Ok(ExitCode::SUCCESS);
|
||||||
};
|
};
|
||||||
let err = &cmd_err.error;
|
let err = &cmd_err.error;
|
||||||
let maybe_hint = cmd_err.hint.as_deref();
|
let hints = &cmd_err.hints;
|
||||||
match cmd_err.kind {
|
match cmd_err.kind {
|
||||||
CommandErrorKind::User => {
|
CommandErrorKind::User => {
|
||||||
print_error(ui, "Error", err, maybe_hint)?;
|
print_error(ui, "Error", err, hints)?;
|
||||||
Ok(ExitCode::from(1))
|
Ok(ExitCode::from(1))
|
||||||
}
|
}
|
||||||
CommandErrorKind::Config => {
|
CommandErrorKind::Config => {
|
||||||
print_error(ui, "Config error", err, maybe_hint)?;
|
print_error(ui, "Config error", err, hints)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
ui.hint(),
|
ui.hint(),
|
||||||
"For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md."
|
"For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md."
|
||||||
|
@ -520,9 +524,9 @@ fn try_handle_command_result(
|
||||||
}
|
}
|
||||||
CommandErrorKind::Cli => {
|
CommandErrorKind::Cli => {
|
||||||
if let Some(err) = err.downcast_ref::<clap::Error>() {
|
if let Some(err) = err.downcast_ref::<clap::Error>() {
|
||||||
handle_clap_error(ui, err, maybe_hint)
|
handle_clap_error(ui, err, hints)
|
||||||
} else {
|
} else {
|
||||||
print_error(ui, "Error", err, maybe_hint)?;
|
print_error(ui, "Error", err, hints)?;
|
||||||
Ok(ExitCode::from(2))
|
Ok(ExitCode::from(2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,21 +535,16 @@ fn try_handle_command_result(
|
||||||
Ok(ExitCode::from(BROKEN_PIPE_EXIT_CODE))
|
Ok(ExitCode::from(BROKEN_PIPE_EXIT_CODE))
|
||||||
}
|
}
|
||||||
CommandErrorKind::Internal => {
|
CommandErrorKind::Internal => {
|
||||||
print_error(ui, "Internal error", err, maybe_hint)?;
|
print_error(ui, "Internal error", err, hints)?;
|
||||||
Ok(ExitCode::from(255))
|
Ok(ExitCode::from(255))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_error(
|
fn print_error(ui: &Ui, prefix: &str, err: &dyn error::Error, hints: &[String]) -> io::Result<()> {
|
||||||
ui: &Ui,
|
|
||||||
prefix: &str,
|
|
||||||
err: &dyn error::Error,
|
|
||||||
maybe_hint: Option<&str>,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
writeln!(ui.error(), "{prefix}: {err}")?;
|
writeln!(ui.error(), "{prefix}: {err}")?;
|
||||||
print_error_sources(ui, err.source())?;
|
print_error_sources(ui, err.source())?;
|
||||||
if let Some(hint) = maybe_hint {
|
for hint in hints {
|
||||||
writeln!(ui.hint(), "Hint: {hint}")?;
|
writeln!(ui.hint(), "Hint: {hint}")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -566,7 +565,7 @@ fn print_error_sources(ui: &Ui, source: Option<&dyn error::Error>) -> io::Result
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_clap_error(ui: &mut Ui, err: &clap::Error, hint: Option<&str>) -> io::Result<ExitCode> {
|
fn handle_clap_error(ui: &mut Ui, err: &clap::Error, hints: &[String]) -> io::Result<ExitCode> {
|
||||||
let clap_str = if ui.color() {
|
let clap_str = if ui.color() {
|
||||||
err.render().ansi().to_string()
|
err.render().ansi().to_string()
|
||||||
} else {
|
} else {
|
||||||
|
@ -588,7 +587,7 @@ fn handle_clap_error(ui: &mut Ui, err: &clap::Error, hint: Option<&str>) -> io::
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
write!(ui.stderr(), "{clap_str}")?;
|
write!(ui.stderr(), "{clap_str}")?;
|
||||||
if let Some(hint) = hint {
|
for hint in hints {
|
||||||
writeln!(ui.hint(), "Hint: {hint}")?;
|
writeln!(ui.hint(), "Hint: {hint}")?;
|
||||||
}
|
}
|
||||||
Ok(ExitCode::from(2))
|
Ok(ExitCode::from(2))
|
||||||
|
|
Loading…
Reference in a new issue