1use std::fmt;
7
8#[derive(Debug)]
11pub enum AacodeError {
12 Config(String),
14 Network(String),
16 Api(String),
18 Tool(String),
20 Json(String),
22 Io(String),
24 Cancelled,
26 Other(String),
28}
29
30impl fmt::Display for AacodeError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 AacodeError::Config(m) => write!(f, "config error: {m}"),
34 AacodeError::Network(m) => write!(f, "network error: {m}"),
35 AacodeError::Api(m) => write!(f, "api error: {m}"),
36 AacodeError::Tool(m) => write!(f, "tool error: {m}"),
37 AacodeError::Json(m) => write!(f, "json error: {m}"),
38 AacodeError::Io(m) => write!(f, "io error: {m}"),
39 AacodeError::Cancelled => write!(f, "cancelled"),
40 AacodeError::Other(m) => write!(f, "{m}"),
41 }
42 }
43}
44
45impl std::error::Error for AacodeError {}
46
47impl From<serde_json::Error> for AacodeError {
48 fn from(e: serde_json::Error) -> Self {
49 AacodeError::Json(e.to_string())
50 }
51}
52
53impl From<std::io::Error> for AacodeError {
54 fn from(e: std::io::Error) -> Self {
55 AacodeError::Io(e.to_string())
56 }
57}
58
59pub type Result<T> = std::result::Result<T, AacodeError>;
61
62impl AacodeError {
63 pub fn is_retryable(&self) -> bool {
65 match self {
66 AacodeError::Network(_) => true,
67 AacodeError::Json(_) => true,
68 AacodeError::Api(m) => {
69 let l = m.to_lowercase();
70 l.contains("timeout")
71 || l.contains("timed out")
72 || l.contains("connection")
73 || l.contains(" 500")
74 || l.contains(" 502")
75 || l.contains(" 503")
76 || l.contains(" 504")
77 || l.contains("rate limit")
78 }
79 _ => false,
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn display_formats() {
90 assert_eq!(AacodeError::Cancelled.to_string(), "cancelled");
91 assert!(AacodeError::Config("x".into()).to_string().contains("config"));
92 }
93
94 #[test]
95 fn retryable_classification() {
96 assert!(AacodeError::Network("reset".into()).is_retryable());
97 assert!(AacodeError::Api("HTTP 503 unavailable".into()).is_retryable());
98 assert!(AacodeError::Api("rate limit exceeded".into()).is_retryable());
99 assert!(AacodeError::Json("malformed response".into()).is_retryable());
100 assert!(!AacodeError::Api("HTTP 401 unauthorized".into()).is_retryable());
101 assert!(!AacodeError::Config("no key".into()).is_retryable());
102 }
103
104 #[test]
105 fn json_error_converts() {
106 let e: AacodeError = serde_json::from_str::<i32>("not json").unwrap_err().into();
107 matches!(e, AacodeError::Json(_));
108 }
109}