agent_tui/common/
error_codes.rs1pub const SESSION_NOT_FOUND: i32 = -32001;
9pub const NO_ACTIVE_SESSION: i32 = -32002;
10pub const SESSION_LIMIT: i32 = -32006;
11pub const LOCK_TIMEOUT: i32 = -32007;
12
13pub const ELEMENT_NOT_FOUND: i32 = -32003;
15pub const WRONG_ELEMENT_TYPE: i32 = -32004;
16
17pub const INVALID_KEY: i32 = -32005;
19pub const PTY_ERROR: i32 = -32008;
20
21pub const WAIT_TIMEOUT: i32 = -32013;
23
24pub const COMMAND_NOT_FOUND: i32 = -32014;
26pub const PERMISSION_DENIED: i32 = -32015;
27
28pub const DAEMON_ERROR: i32 = -32016;
30pub const PERSISTENCE_ERROR: i32 = -32017;
31
32pub const GENERIC_ERROR: i32 = -32000;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ErrorCategory {
38 NotFound,
40 InvalidInput,
42 Busy,
44 Internal,
46 External,
48 Timeout,
50}
51
52impl ErrorCategory {
53 pub fn as_str(&self) -> &'static str {
54 match self {
55 ErrorCategory::NotFound => "not_found",
56 ErrorCategory::InvalidInput => "invalid_input",
57 ErrorCategory::Busy => "busy",
58 ErrorCategory::Internal => "internal",
59 ErrorCategory::External => "external",
60 ErrorCategory::Timeout => "timeout",
61 }
62 }
63}
64
65impl std::str::FromStr for ErrorCategory {
66 type Err = ();
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 match s {
70 "not_found" => Ok(ErrorCategory::NotFound),
71 "invalid_input" => Ok(ErrorCategory::InvalidInput),
72 "busy" => Ok(ErrorCategory::Busy),
73 "internal" => Ok(ErrorCategory::Internal),
74 "external" => Ok(ErrorCategory::External),
75 "timeout" => Ok(ErrorCategory::Timeout),
76 _ => Err(()),
77 }
78 }
79}
80
81impl std::fmt::Display for ErrorCategory {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{}", self.as_str())
84 }
85}
86
87pub fn is_retryable(code: i32) -> bool {
93 matches!(code, LOCK_TIMEOUT | GENERIC_ERROR)
94}
95
96pub fn category_for_code(code: i32) -> ErrorCategory {
98 match code {
99 SESSION_NOT_FOUND | NO_ACTIVE_SESSION | ELEMENT_NOT_FOUND => ErrorCategory::NotFound,
100 WRONG_ELEMENT_TYPE | INVALID_KEY => ErrorCategory::InvalidInput,
101 SESSION_LIMIT | LOCK_TIMEOUT => ErrorCategory::Busy,
102 PTY_ERROR | COMMAND_NOT_FOUND | PERMISSION_DENIED | DAEMON_ERROR | PERSISTENCE_ERROR => {
103 ErrorCategory::External
104 }
105 WAIT_TIMEOUT => ErrorCategory::Timeout,
106 _ => ErrorCategory::Internal,
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_is_retryable_lock_timeout() {
116 assert!(is_retryable(LOCK_TIMEOUT));
117 }
118
119 #[test]
120 fn test_is_retryable_generic() {
121 assert!(is_retryable(GENERIC_ERROR));
122 }
123
124 #[test]
125 fn test_not_retryable_session_not_found() {
126 assert!(!is_retryable(SESSION_NOT_FOUND));
127 }
128
129 #[test]
130 fn test_not_retryable_element_not_found() {
131 assert!(!is_retryable(ELEMENT_NOT_FOUND));
132 }
133
134 #[test]
135 fn test_category_for_code_not_found() {
136 assert_eq!(
137 category_for_code(SESSION_NOT_FOUND),
138 ErrorCategory::NotFound
139 );
140 assert_eq!(
141 category_for_code(NO_ACTIVE_SESSION),
142 ErrorCategory::NotFound
143 );
144 assert_eq!(
145 category_for_code(ELEMENT_NOT_FOUND),
146 ErrorCategory::NotFound
147 );
148 }
149
150 #[test]
151 fn test_category_for_code_invalid_input() {
152 assert_eq!(
153 category_for_code(WRONG_ELEMENT_TYPE),
154 ErrorCategory::InvalidInput
155 );
156 assert_eq!(category_for_code(INVALID_KEY), ErrorCategory::InvalidInput);
157 }
158
159 #[test]
160 fn test_category_for_code_busy() {
161 assert_eq!(category_for_code(SESSION_LIMIT), ErrorCategory::Busy);
162 assert_eq!(category_for_code(LOCK_TIMEOUT), ErrorCategory::Busy);
163 }
164
165 #[test]
166 fn test_category_for_code_external() {
167 assert_eq!(category_for_code(PTY_ERROR), ErrorCategory::External);
168 assert_eq!(
169 category_for_code(COMMAND_NOT_FOUND),
170 ErrorCategory::External
171 );
172 assert_eq!(
173 category_for_code(PERMISSION_DENIED),
174 ErrorCategory::External
175 );
176 assert_eq!(category_for_code(DAEMON_ERROR), ErrorCategory::External);
177 assert_eq!(
178 category_for_code(PERSISTENCE_ERROR),
179 ErrorCategory::External
180 );
181 }
182
183 #[test]
184 fn test_category_for_code_timeout() {
185 assert_eq!(category_for_code(WAIT_TIMEOUT), ErrorCategory::Timeout);
186 }
187
188 #[test]
189 fn test_category_as_str() {
190 assert_eq!(ErrorCategory::NotFound.as_str(), "not_found");
191 assert_eq!(ErrorCategory::InvalidInput.as_str(), "invalid_input");
192 assert_eq!(ErrorCategory::Busy.as_str(), "busy");
193 assert_eq!(ErrorCategory::Internal.as_str(), "internal");
194 assert_eq!(ErrorCategory::External.as_str(), "external");
195 assert_eq!(ErrorCategory::Timeout.as_str(), "timeout");
196 }
197
198 #[test]
199 fn test_category_from_str() {
200 assert_eq!(
201 "not_found".parse::<ErrorCategory>(),
202 Ok(ErrorCategory::NotFound)
203 );
204 assert_eq!(
205 "invalid_input".parse::<ErrorCategory>(),
206 Ok(ErrorCategory::InvalidInput)
207 );
208 assert_eq!("busy".parse::<ErrorCategory>(), Ok(ErrorCategory::Busy));
209 assert_eq!(
210 "internal".parse::<ErrorCategory>(),
211 Ok(ErrorCategory::Internal)
212 );
213 assert_eq!(
214 "external".parse::<ErrorCategory>(),
215 Ok(ErrorCategory::External)
216 );
217 assert_eq!(
218 "timeout".parse::<ErrorCategory>(),
219 Ok(ErrorCategory::Timeout)
220 );
221 assert!("unknown".parse::<ErrorCategory>().is_err());
222 }
223}