cli_command/
cli_error.rs

1use std::error::Error;
2use std::fmt;
3
4/// The different kinds of errors that can occur during command line parsing.
5#[derive(Clone, Debug)]
6pub enum CliErrorKind {
7    /// A required argument was not provided
8    MissingArgument(String),
9    /// A required parameter at a specific position was not provided
10    MissingParameter(String, usize),
11    /// General command line parsing error
12    ParseCommandLine,
13    /// An internal error occurred (e.g., during type conversion)
14    Inner,
15}
16
17/// An error that occurred during command line parsing or argument processing.
18///
19/// This error type provides detailed information about what went wrong during
20/// command line parsing, including helpful error messages for users.
21#[derive(Debug)]
22pub struct CliError {
23    /// The underlying error that caused this error (if any)
24    pub source: Option<Box<dyn Error>>,
25    /// The specific kind of error that occurred
26    pub kind: CliErrorKind,
27}
28
29impl CliError {
30    /// Creates a new `CliError` with an underlying source error.
31    ///
32    /// This is typically used when an error occurs during type conversion
33    /// or other internal operations.
34    ///
35    /// # Arguments
36    /// * `source` - The underlying error that caused this error
37    ///
38    /// # Returns
39    /// A new `CliError` with `kind` set to `Inner` and the provided source error.
40    pub fn new_inner(source: Box<dyn Error>) -> Self {
41        CliError {
42            source: Some(source),
43            kind: CliErrorKind::Inner,
44        }
45    }
46
47    /// Creates a new `CliError` with a specific error kind.
48    ///
49    /// This is typically used for command line parsing errors that don't
50    /// have an underlying source error.
51    ///
52    /// # Arguments
53    /// * `kind` - The specific kind of error that occurred
54    ///
55    /// # Returns
56    /// A new `CliError` with the provided kind and no source error.
57    pub fn new_kind(kind: CliErrorKind) -> Self {
58        CliError { source: None, kind }
59    }
60}
61
62impl Error for CliError {
63    fn source(&self) -> Option<&(dyn Error + 'static)> {
64        self.source.as_deref()
65    }
66}
67
68impl CliErrorKind {
69    pub fn into_boxed_error(self) -> Box<dyn Error> {
70        let error: CliError = self.into();
71        error.into()
72    }
73}
74
75impl fmt::Display for CliErrorKind {
76    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77        match self {
78            CliErrorKind::MissingArgument(arg) =>
79                write!(f, "Required argument '{}' not provided. Use --{} <value> to specify it.", arg, arg),
80            CliErrorKind::MissingParameter(parameter, position) =>
81                write!(f, "Missing parameter for '{}' at position {}. Expected: --{} <value1> <value2> ...", parameter, position, parameter),
82            CliErrorKind::ParseCommandLine =>
83                write!(f, "Failed to parse command line. Ensure arguments are properly formatted with - or -- prefixes."),
84            CliErrorKind::Inner =>
85                write!(f, "Internal error occurred during argument processing"),
86        }
87    }
88}
89
90impl fmt::Display for CliError {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        write!(f, "{}", self.kind)
93    }
94}
95
96impl From<CliErrorKind> for CliError {
97    fn from(kind: CliErrorKind) -> Self {
98        Self::new_kind(kind)
99    }
100}
101
102pub fn from_error<T>(error: T) -> CliError
103where
104    T: Error + Send + Sync + 'static,
105{
106    CliError::new_inner(Box::new(error))
107}
108
109macro_rules! impl_from_error {
110    ($($error_type:ty),+ $(,)?) => {
111        $(
112            impl From<$error_type> for CliError {
113                fn from(error: $error_type) -> Self {
114                    from_error(error)
115                }
116            }
117        )+
118    };
119}
120
121impl_from_error!(
122    std::io::Error,
123    std::net::AddrParseError,
124    std::num::ParseIntError,
125    std::str::ParseBoolError,
126    std::char::ParseCharError,
127    std::string::ParseError,
128    std::num::ParseFloatError,
129);
130
131impl From<Box<dyn Error>> for CliError {
132    fn from(error: Box<dyn Error>) -> Self {
133        Self::new_inner(error)
134    }
135}