use std::fmt;
#[derive(Debug)]
pub enum ConnectError {
InvalidUrl,
InvalidScheme(String),
LaunchError(String),
ConnectionFailed(String),
Timeout(String),
}
impl fmt::Display for ConnectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConnectError::InvalidUrl => write!(f, "invalid URL (empty or missing scheme)"),
ConnectError::InvalidScheme(scheme) => {
write!(
f,
"invalid URL scheme: {:?} (expected memory/ws/wss/http/https)",
scheme
)
}
ConnectError::LaunchError(msg) => write!(f, "browser launch failed: {}", msg),
ConnectError::ConnectionFailed(msg) => write!(f, "connection failed: {}", msg),
ConnectError::Timeout(msg) => write!(f, "connect timeout: {}", msg),
}
}
}
impl std::error::Error for ConnectError {}
impl From<std::io::Error> for ConnectError {
fn from(err: std::io::Error) -> Self {
ConnectError::ConnectionFailed(err.to_string())
}
}
#[derive(Debug)]
pub enum CdpError {
ProtocolError(String),
JsonError(String),
IoError(std::io::Error),
ConnectionClosed,
Timeout(String),
TransportError(String),
HandshakeError(String),
}
impl fmt::Display for CdpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CdpError::ProtocolError(msg) => write!(f, "CDP protocol error: {}", msg),
CdpError::JsonError(msg) => write!(f, "JSON error: {}", msg),
CdpError::IoError(err) => write!(f, "I/O error: {}", err),
CdpError::ConnectionClosed => write!(f, "connection closed"),
CdpError::Timeout(msg) => write!(f, "timeout: {}", msg),
CdpError::TransportError(msg) => write!(f, "transport error: {}", msg),
CdpError::HandshakeError(msg) => write!(f, "WebSocket handshake error: {}", msg),
}
}
}
impl std::error::Error for CdpError {}
impl From<std::io::Error> for CdpError {
fn from(err: std::io::Error) -> Self {
CdpError::IoError(err)
}
}
impl From<serde_json::Error> for CdpError {
fn from(err: serde_json::Error) -> Self {
CdpError::JsonError(err.to_string())
}
}
impl From<ConnectError> for CdpError {
fn from(err: ConnectError) -> Self {
CdpError::ProtocolError(err.to_string())
}
}
pub type Result<T> = std::result::Result<T, CdpError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn connect_error_invalid_url_display() {
let err = ConnectError::InvalidUrl;
let s = err.to_string();
assert!(s.contains("invalid URL"), "got: {}", s);
}
#[test]
fn connect_error_invalid_scheme_carries_scheme() {
let err = ConnectError::InvalidScheme("ftp".to_string());
let s = err.to_string();
assert!(s.contains("ftp"), "got: {}", s);
assert!(s.contains("invalid URL scheme"), "got: {}", s);
}
#[test]
fn connect_error_launch_message() {
let err = ConnectError::LaunchError("chrome not found".to_string());
assert!(err.to_string().contains("chrome not found"));
}
#[test]
fn connect_error_connection_failed_message() {
let err = ConnectError::ConnectionFailed("refused".to_string());
assert!(err.to_string().contains("refused"));
}
#[test]
fn connect_error_timeout_message() {
let err = ConnectError::Timeout("30s elapsed".to_string());
let s = err.to_string();
assert!(s.contains("timeout"), "got: {}", s);
assert!(s.contains("30s elapsed"), "got: {}", s);
}
#[test]
fn connect_error_is_std_error() {
let err = ConnectError::InvalidUrl;
let _: &dyn std::error::Error = &err;
}
#[test]
fn cdp_error_from_connect_error() {
let err = ConnectError::InvalidScheme("foo".into());
let cdp: CdpError = err.into();
assert!(cdp.to_string().contains("invalid URL scheme"));
}
#[test]
fn cdp_error_from_serde_json() {
let json_err = serde_json::from_str::<serde_json::Value>("not json").unwrap_err();
let cdp: CdpError = json_err.into();
assert!(matches!(cdp, CdpError::JsonError(_)));
}
#[test]
fn cdp_error_from_io() {
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "boom");
let cdp: CdpError = io_err.into();
assert!(matches!(cdp, CdpError::IoError(_)));
}
#[test]
fn cdp_error_connection_closed() {
let err = CdpError::ConnectionClosed;
assert_eq!(err.to_string(), "connection closed");
}
#[test]
fn cdp_error_transport_error_message() {
let err = CdpError::TransportError("channel closed".into());
let s = err.to_string();
assert!(s.contains("transport error"), "got: {}", s);
assert!(s.contains("channel closed"), "got: {}", s);
}
#[test]
fn cdp_error_handshake_error_message() {
let err = CdpError::HandshakeError("missing key".into());
let s = err.to_string();
assert!(s.contains("WebSocket handshake error"), "got: {}", s);
assert!(s.contains("missing key"), "got: {}", s);
}
}