1use std::fmt;
2
3#[derive(Debug)]
5pub enum CdpError {
6 Connection(String),
8
9 ConnectionTimeout,
11
12 CommandTimeout {
14 method: String,
16 },
17
18 Protocol {
20 code: i64,
22 message: String,
24 },
25
26 ConnectionClosed,
28
29 InvalidResponse(String),
31
32 ReconnectFailed {
34 attempts: u32,
36 last_error: String,
38 },
39
40 Internal(String),
42}
43
44impl fmt::Display for CdpError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 Self::Connection(msg) => write!(f, "CDP connection error: {msg}"),
48 Self::ConnectionTimeout => write!(f, "CDP connection timed out"),
49 Self::CommandTimeout { method } => {
50 write!(f, "CDP command timed out: {method}")
51 }
52 Self::Protocol { code, message } => {
53 write!(f, "CDP protocol error ({code}): {message}")
54 }
55 Self::ConnectionClosed => write!(f, "CDP connection closed"),
56 Self::InvalidResponse(msg) => {
57 write!(f, "CDP invalid response: {msg}")
58 }
59 Self::ReconnectFailed {
60 attempts,
61 last_error,
62 } => {
63 write!(
64 f,
65 "CDP reconnection failed after {attempts} attempts: {last_error}"
66 )
67 }
68 Self::Internal(msg) => write!(f, "CDP internal error: {msg}"),
69 }
70 }
71}
72
73impl std::error::Error for CdpError {}
74
75impl From<CdpError> for crate::error::AppError {
76 fn from(e: CdpError) -> Self {
77 use crate::error::ExitCode;
78 let code = match &e {
79 CdpError::Connection(_)
80 | CdpError::ConnectionClosed
81 | CdpError::ReconnectFailed { .. } => ExitCode::ConnectionError,
82 CdpError::ConnectionTimeout | CdpError::CommandTimeout { .. } => ExitCode::TimeoutError,
83 CdpError::Protocol { .. } => ExitCode::ProtocolError,
84 CdpError::InvalidResponse(_) | CdpError::Internal(_) => ExitCode::GeneralError,
85 };
86 Self {
87 message: e.to_string(),
88 code,
89 custom_json: None,
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn display_connection() {
100 let err = CdpError::Connection("refused".into());
101 assert_eq!(err.to_string(), "CDP connection error: refused");
102 }
103
104 #[test]
105 fn display_connection_timeout() {
106 let err = CdpError::ConnectionTimeout;
107 assert_eq!(err.to_string(), "CDP connection timed out");
108 }
109
110 #[test]
111 fn display_command_timeout() {
112 let err = CdpError::CommandTimeout {
113 method: "Page.navigate".into(),
114 };
115 assert_eq!(err.to_string(), "CDP command timed out: Page.navigate");
116 }
117
118 #[test]
119 fn display_protocol() {
120 let err = CdpError::Protocol {
121 code: -32000,
122 message: "Not found".into(),
123 };
124 assert_eq!(err.to_string(), "CDP protocol error (-32000): Not found");
125 }
126
127 #[test]
128 fn display_connection_closed() {
129 let err = CdpError::ConnectionClosed;
130 assert_eq!(err.to_string(), "CDP connection closed");
131 }
132
133 #[test]
134 fn display_invalid_response() {
135 let err = CdpError::InvalidResponse("bad json".into());
136 assert_eq!(err.to_string(), "CDP invalid response: bad json");
137 }
138
139 #[test]
140 fn display_reconnect_failed() {
141 let err = CdpError::ReconnectFailed {
142 attempts: 3,
143 last_error: "connection refused".into(),
144 };
145 assert_eq!(
146 err.to_string(),
147 "CDP reconnection failed after 3 attempts: connection refused"
148 );
149 }
150
151 #[test]
152 fn display_internal() {
153 let err = CdpError::Internal("channel closed".into());
154 assert_eq!(err.to_string(), "CDP internal error: channel closed");
155 }
156
157 #[test]
158 fn error_trait_is_implemented() {
159 let err: &dyn std::error::Error = &CdpError::ConnectionClosed;
160 assert!(err.source().is_none());
162 }
163}