1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

uucore/error: add custom exit codes for clap errors

This allows us to use clap errors as UResult and specify the exit code for invalid arguments per util.
This commit is contained in:
Terts Diepraam 2022-04-30 11:25:53 +02:00
parent a78f86a4f5
commit 7b84261df4

View file

@ -617,12 +617,75 @@ impl From<i32> for Box<dyn UError> {
} }
} }
/// Implementations for clap::Error /// A wrapper for `clap::Error` that implements [`UError`]
impl UError for clap::Error { ///
/// Contains a custom error code. When `Display::fmt` is called on this struct
/// the [`clap::Error`] will be printed _directly to `stdout` or `stderr`_.
/// This is because `clap` only supports colored output when it prints directly.
///
/// [`ClapErrorWrapper`] is generally created by calling the
/// [`UClapError::with_exit_code`] method on [`clap::Error`] or using the [`From`]
/// implementation from [`clap::Error`] to `Box<dyn UError>`, which constructs
/// a [`ClapErrorWrapper`] with an exit code of `1`.
///
/// ```rust
/// use uucore::error::{ClapErrorWrapper, UError, UClapError};
/// let command = clap::Command::new("test");
/// let result: Result<_, ClapErrorWrapper> = command.try_get_matches().with_exit_code(125);
///
/// let command = clap::Command::new("test");
/// let result: Result<_, Box<dyn UError>> = command.try_get_matches().map_err(Into::into);
/// ```
#[derive(Debug)]
pub struct ClapErrorWrapper {
code: i32,
error: clap::Error,
}
/// Extension trait for `clap::Error` to adjust the exit code.
pub trait UClapError<T> {
fn with_exit_code(self, code: i32) -> T;
}
impl From<clap::Error> for Box<dyn UError> {
fn from(e: clap::Error) -> Self {
Box::new(ClapErrorWrapper { code: 1, error: e })
}
}
impl UClapError<ClapErrorWrapper> for clap::Error {
fn with_exit_code(self, code: i32) -> ClapErrorWrapper {
ClapErrorWrapper { code, error: self }
}
}
impl UClapError<Result<clap::ArgMatches, ClapErrorWrapper>>
for Result<clap::ArgMatches, clap::Error>
{
fn with_exit_code(self, code: i32) -> Result<clap::ArgMatches, ClapErrorWrapper> {
self.map_err(|e| e.with_exit_code(code))
}
}
impl UError for ClapErrorWrapper {
fn code(&self) -> i32 { fn code(&self) -> i32 {
match self.kind() { // If the error is a DisplayHelp or DisplayVersion variant,
clap::ErrorKind::DisplayHelp | clap::ErrorKind::DisplayVersion => 0, // we don't want to apply the custom error code, but leave
_ => 1, // it 0.
if let clap::ErrorKind::DisplayHelp | clap::ErrorKind::DisplayVersion = self.error.kind() {
0
} else {
self.code
} }
} }
} }
impl Error for ClapErrorWrapper {}
// This is abuse of the Display trait
impl Display for ClapErrorWrapper {
fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.error.print().unwrap();
Ok(())
}
}