Skip to main content

cli_forge/
error.rs

1//! Parse errors.
2//!
3//! Every way command-line input can be malformed maps to a [`ParseError`]
4//! variant — never a panic. The error carries enough context to tell the user
5//! what was wrong (which flag, which argument), and it renders through the same
6//! output system as everything else: [`App::parse`](crate::App::parse) prints it
7//! to standard error and exits, while
8//! [`App::try_parse_from`](crate::App::try_parse_from) hands it back for the
9//! caller to handle.
10
11use std::error::Error;
12use std::fmt;
13
14/// A failure to parse command-line arguments.
15///
16/// Returned by [`App::try_parse_from`](crate::App::try_parse_from). The variants
17/// are marked `#[non_exhaustive]` so future versions can add cases (for example,
18/// as the help and auth seams land) without a breaking change.
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum ParseError {
22    /// A `-x` / `--name` flag was given that no argument at this level declares.
23    UnknownFlag {
24        /// The flag as the user wrote it, e.g. `--verbsoe`.
25        flag: String,
26    },
27    /// An option that takes a value was given without one (it was the last
28    /// token, or followed only by another option).
29    MissingValue {
30        /// The option's name.
31        option: String,
32    },
33    /// A required argument was not provided.
34    MissingRequired {
35        /// The missing argument's name.
36        arg: String,
37    },
38    /// A subcommand name was expected but the token matched no registered
39    /// command.
40    UnknownCommand {
41        /// The unrecognized command name.
42        name: String,
43    },
44    /// A bare value was given that no positional argument or subcommand can
45    /// accept.
46    UnexpectedArgument {
47        /// The surplus value.
48        value: String,
49    },
50    /// Not an error: `-h` / `--help` was requested. Carries the rendered help
51    /// text. [`App::parse`](crate::App::parse) prints it to standard output and
52    /// exits `0`; callers of
53    /// [`App::try_parse_from`](crate::App::try_parse_from) should do the same.
54    HelpRequested(String),
55    /// Not an error: `-V` / `--version` was requested. Carries the version
56    /// string. Handled like [`HelpRequested`](ParseError::HelpRequested).
57    VersionRequested(String),
58    /// An auth-gated command ([`Command::requires_auth`](crate::Command::requires_auth))
59    /// was invoked but the app's auth hook did not authorize it (or no hook was
60    /// set). Produced only with the `auth` feature enabled; the command's handler
61    /// does not run.
62    Unauthorized {
63        /// The command that was refused.
64        command: String,
65    },
66}
67
68impl fmt::Display for ParseError {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match self {
71            ParseError::UnknownFlag { flag } => write!(f, "unknown flag: {flag}"),
72            ParseError::MissingValue { option } => {
73                write!(f, "missing value for option: {option}")
74            }
75            ParseError::MissingRequired { arg } => {
76                write!(f, "missing required argument: {arg}")
77            }
78            ParseError::UnknownCommand { name } => write!(f, "unknown command: {name}"),
79            ParseError::UnexpectedArgument { value } => {
80                write!(f, "unexpected argument: {value}")
81            }
82            ParseError::HelpRequested(text) | ParseError::VersionRequested(text) => {
83                write!(f, "{text}")
84            }
85            ParseError::Unauthorized { command } => {
86                write!(f, "not authorized to run: {command}")
87            }
88        }
89    }
90}
91
92impl Error for ParseError {}