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)]
53pub struct CliError {
54 pub rule: CliErrorRule,
55 pub command_path: String,
56 pub argument_names: Vec<String>,
57 pub message: String,
58 pub hint: String,
59}
60
61impl CliError {
62 pub(super) fn new(
63 rule: CliErrorRule,
64 command_path: String,
65 argument_names: Vec<String>,
66 message: impl Into<String>,
67 ) -> Self {
68 let hint = format!("run `{command_path} --help` and choose one registered combination");
69 Self {
70 rule,
71 command_path,
72 argument_names,
73 message: message.into(),
74 hint,
75 }
76 }
77
78 pub(super) fn unregistered(command_path: String, argument_names: Vec<String>) -> Self {
79 Self::new(
80 CliErrorRule::UnregisteredCombination,
81 command_path,
82 argument_names,
83 "arguments do not match a registered CLI combination",
84 )
85 }
86
87 pub const fn exit_code(&self) -> u8 {
92 2
93 }
94}
95
96impl std::fmt::Display for CliError {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 f.write_str(&self.message)
99 }
100}
101
102impl std::error::Error for CliError {}
103
104pub(super) fn invalid_value(command_path: &str, name: String, message: &str) -> CliError {
105 CliError::new(
106 CliErrorRule::InvalidArgumentValue,
107 command_path.to_string(),
108 vec![name.clone()],
109 format!("invalid value for `{name}`: {message}"),
110 )
111}
112
113pub(super) fn missing_value(command_path: &str, name: &str) -> CliError {
114 CliError::new(
115 CliErrorRule::MissingArgumentValue,
116 command_path.to_string(),
117 vec![name.to_string()],
118 format!("argument `{name}` requires a value"),
119 )
120}
121
122pub(super) fn duplicate_error(command_path: &str, name: &str) -> CliError {
123 CliError::new(
124 CliErrorRule::DuplicateArgument,
125 command_path.to_string(),
126 vec![name.to_string()],
127 format!("argument `{name}` may not be repeated"),
128 )
129}