use thiserror::Error;
pub type Result<T> = std::result::Result<T, BrowserError>;
#[derive(Error, Debug)]
pub enum BrowserError {
#[error("WebSocket error during {operation}: {message}")]
WebSocket {
operation: String,
message: String,
},
#[error("Failed to connect to '{endpoint}': {reason}")]
ConnectionFailed {
endpoint: String,
reason: String,
},
#[error("Invalid CDP response while {context}: {details}")]
InvalidResponse {
context: String,
details: String,
},
#[error("Command '{command}' failed: {reason}")]
CommandFailed {
command: String,
reason: String,
},
#[error("CDP error {code} in '{method}': {message}")]
CdpError {
code: i32,
method: String,
message: String,
},
#[error("Timed out {operation} after {timeout_secs}s")]
Timeout {
operation: String,
timeout_secs: u64,
},
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Page not found: {0}")]
PageNotFound(String),
#[error("Target not found: {0}")]
TargetNotFound(String),
#[error("Browser not launched: {0}")]
BrowserNotLaunched(String),
#[error("Navigation to '{url}' failed: {reason}")]
NavigationFailed {
url: String,
reason: String,
},
}
impl BrowserError {
pub fn websocket(operation: impl Into<String>, message: impl Into<String>) -> Self {
Self::WebSocket {
operation: operation.into(),
message: message.into(),
}
}
pub fn connection_failed(endpoint: impl Into<String>, reason: impl Into<String>) -> Self {
Self::ConnectionFailed {
endpoint: endpoint.into(),
reason: reason.into(),
}
}
pub fn command_failed(command: impl Into<String>, reason: impl Into<String>) -> Self {
Self::CommandFailed {
command: command.into(),
reason: reason.into(),
}
}
pub fn invalid_response(context: impl Into<String>, details: impl Into<String>) -> Self {
Self::InvalidResponse {
context: context.into(),
details: details.into(),
}
}
pub fn timeout(operation: impl Into<String>, timeout_secs: u64) -> Self {
Self::Timeout {
operation: operation.into(),
timeout_secs,
}
}
pub fn navigation_failed(url: impl Into<String>, reason: impl Into<String>) -> Self {
Self::NavigationFailed {
url: url.into(),
reason: reason.into(),
}
}
}
pub trait ResultExt<T> {
fn context(self, ctx: impl Into<String>) -> Result<T>;
}
impl<T> ResultExt<T> for Result<T> {
fn context(self, ctx: impl Into<String>) -> Result<T> {
self.map_err(|e| BrowserError::CommandFailed {
command: ctx.into(),
reason: e.to_string(),
})
}
}