use std::fmt;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("browser error: {0}")]
Browser(String),
#[error("navigation error: {0}")]
Navigation(String),
#[error("element not found: {selector}")]
ElementNotFound {
selector: String,
},
#[error("authentication failed for provider {provider}: {reason}")]
AuthenticationFailed {
provider: String,
reason: String,
},
#[error("session expired for provider {0}")]
SessionExpired(String),
#[error("rate limit exceeded, retry after {retry_after_secs}s")]
RateLimitExceeded {
retry_after_secs: u64,
},
#[error("provider error from {provider}: {message}")]
ProviderError {
provider: String,
message: String,
},
#[error("failed to extract response: {0}")]
ExtractionFailed(String),
#[error("credential error: {0}")]
Credential(String),
#[error("configuration error: {0}")]
Config(String),
#[error("timeout after {0}ms")]
Timeout(u64),
#[error("provider {0} not supported")]
UnsupportedProvider(String),
#[error("browser not found at path: {0}")]
BrowserNotFound(String),
#[error("permission denied for {operation}: {reason}")]
PermissionDenied {
operation: String,
reason: String,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[cfg(feature = "chromium")]
#[error("chromium error: {0}")]
Chromium(String),
#[cfg(feature = "firefox")]
#[error("webdriver error: {0}")]
WebDriver(String),
#[error("intervention cancelled by user")]
InterventionCancelled,
#[error("intervention timed out after {0}s")]
InterventionTimeout(u64),
#[error("internal error: {0}")]
Internal(String),
}
impl Error {
pub fn is_retryable(&self) -> bool {
matches!(
self,
Error::Navigation(_)
| Error::RateLimitExceeded { .. }
| Error::Timeout(_)
| Error::Browser(_)
)
}
pub fn retry_delay_secs(&self) -> Option<u64> {
match self {
Error::RateLimitExceeded { retry_after_secs } => Some(*retry_after_secs),
Error::Timeout(_) => Some(5),
Error::Navigation(_) | Error::Browser(_) => Some(10),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct ErrorContext {
pub operation: String,
pub provider: Option<String>,
pub url: Option<String>,
pub details: Option<String>,
}
impl fmt::Display for ErrorContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "operation: {}", self.operation)?;
if let Some(ref p) = self.provider {
write!(f, ", provider: {}", p)?;
}
if let Some(ref u) = self.url {
write!(f, ", url: {}", u)?;
}
if let Some(ref d) = self.details {
write!(f, ", details: {}", d)?;
}
Ok(())
}
}