agent_first_data/cli_spec/
error.rs1use serde::Serialize;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
5#[serde(rename_all = "snake_case")]
6pub enum CliErrorRule {
7 InvalidUtf8,
8 UnknownCommand,
9 UnknownArgument,
10 MissingArgumentValue,
11 InvalidArgumentValue,
12 DuplicateArgument,
13 UnexpectedPositional,
14 UnregisteredCombination,
15}
16
17impl CliErrorRule {
18 pub const fn as_str(self) -> &'static str {
19 match self {
20 Self::InvalidUtf8 => "invalid_utf8",
21 Self::UnknownCommand => "unknown_command",
22 Self::UnknownArgument => "unknown_argument",
23 Self::MissingArgumentValue => "missing_argument_value",
24 Self::InvalidArgumentValue => "invalid_argument_value",
25 Self::DuplicateArgument => "duplicate_argument",
26 Self::UnexpectedPositional => "unexpected_positional",
27 Self::UnregisteredCombination => "unregistered_combination",
28 }
29 }
30
31 pub const fn code(self) -> &'static str {
38 match self {
39 Self::InvalidUtf8 => "cli_invalid_utf8",
40 Self::UnknownCommand => "cli_unknown_command",
41 Self::UnknownArgument => "cli_unknown_argument",
42 Self::MissingArgumentValue => "cli_missing_argument_value",
43 Self::InvalidArgumentValue => "cli_invalid_argument_value",
44 Self::DuplicateArgument => "cli_duplicate_argument",
45 Self::UnexpectedPositional => "cli_unexpected_positional",
46 Self::UnregisteredCombination => "cli_unregistered_combination",
47 }
48 }
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
58pub struct CliError {
59 pub rule: CliErrorRule,
60 pub message: String,
61 pub hint: String,
62}
63
64impl CliError {
65 pub(super) fn new(
66 rule: CliErrorRule,
67 command_path: String,
68 message: impl Into<String>,
69 ) -> Self {
70 let hint = format!("run `{command_path} --help` and choose one registered combination");
71 Self {
72 rule,
73 message: message.into(),
74 hint,
75 }
76 }
77
78 pub(super) fn unregistered(command_path: String) -> Self {
79 Self::new(
80 CliErrorRule::UnregisteredCombination,
81 command_path,
82 "arguments do not match a registered CLI combination",
83 )
84 }
85
86 pub const fn exit_code(&self) -> u8 {
91 2
92 }
93}
94
95impl std::fmt::Display for CliError {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.write_str(&self.message)
98 }
99}
100
101impl std::error::Error for CliError {}
102
103pub(super) fn invalid_value(command_path: &str, name: String, message: &str) -> CliError {
104 CliError::new(
105 CliErrorRule::InvalidArgumentValue,
106 command_path.to_string(),
107 format!("invalid value for `{name}`: {message}"),
108 )
109}
110
111pub(super) fn missing_value(command_path: &str, name: &str) -> CliError {
112 CliError::new(
113 CliErrorRule::MissingArgumentValue,
114 command_path.to_string(),
115 format!("argument `{name}` requires a value"),
116 )
117}
118
119pub(super) fn duplicate_error(command_path: &str, name: &str) -> CliError {
120 CliError::new(
121 CliErrorRule::DuplicateArgument,
122 command_path.to_string(),
123 format!("argument `{name}` may not be repeated"),
124 )
125}