use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AuditError {
#[error("Audit interrupted by {signal}; in-progress work was discarded")]
Interrupted { signal: String },
#[error("No compatible browser found!\n\nSupported browsers: Chrome, Edge, Chromium\n\nInstallation options:\n macOS: brew install --cask google-chrome\n Linux: sudo apt install chromium-browser\n Windows: Download from https://www.google.com/chrome/\n\nOr install a managed browser:\n auditmysite browser install\n\nOr specify manually:\n auditmysite --browser-path /path/to/chrome <url>\n\nRun diagnostics:\n auditmysite doctor")]
ChromeNotFound,
#[error("Chrome binary at '{path}' is not executable. Try: chmod +x {path}")]
ChromeNotExecutable { path: PathBuf },
#[error("Failed to launch browser: {reason}")]
BrowserLaunchFailed { reason: String },
#[error("Failed to navigate to '{url}': {reason}")]
NavigationFailed { url: String, reason: String },
#[error("Access to '{url}' was blocked: {reason}. That page is not the site's content and was not audited")]
AccessBlocked { url: String, reason: String },
#[error("Page load timeout for '{url}' after {timeout_secs} seconds")]
PageLoadTimeout { url: String, timeout_secs: u64 },
#[error("Failed to extract accessibility tree: {reason}")]
AXTreeExtractionFailed { reason: String },
#[error("Invalid URL: {url} - {reason}")]
InvalidUrl { url: String, reason: String },
#[error("Failed to parse sitemap from '{url}': {reason}")]
SitemapParseFailed { url: String, reason: String },
#[error("File operation failed for '{path}': {reason}")]
FileError { path: PathBuf, reason: String },
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("CDP error: {0}")]
CdpError(String),
#[error("Failed to generate report: {reason}")]
ReportGenerationFailed { reason: String },
#[error("Output error: {reason}")]
OutputError { reason: String },
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("URL parse error: {0}")]
UrlParseError(#[from] url::ParseError),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("HTTP request failed: {0}")]
HttpError(#[from] reqwest::Error),
#[error("Browser automation error: {0}")]
ChromiumError(String),
#[error("Browser pool timeout: no page available after {timeout_secs} seconds")]
PoolTimeout { timeout_secs: u64 },
#[error("Browser pool has been closed")]
PoolClosed,
#[error("Browser pool exhausted: all pages are in use")]
PoolExhausted,
#[error("Interaction failed: {reason}")]
InteractionFailed { reason: String },
#[error("Audit timed out for '{url}' after {timeout_secs} seconds")]
AuditTimeout { url: String, timeout_secs: u64 },
}
pub type Result<T> = std::result::Result<T, AuditError>;
impl From<chromiumoxide::error::CdpError> for AuditError {
fn from(err: chromiumoxide::error::CdpError) -> Self {
AuditError::CdpError(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chrome_not_found_error_message() {
let err = AuditError::ChromeNotFound;
let msg = err.to_string();
assert!(msg.contains("No compatible browser found"));
assert!(msg.contains("brew install"));
assert!(msg.contains("auditmysite browser install"));
assert!(msg.contains("--browser-path"));
assert!(msg.contains("auditmysite doctor"));
}
#[test]
fn test_navigation_failed_error() {
let err = AuditError::NavigationFailed {
url: "https://example.com".to_string(),
reason: "Connection refused".to_string(),
};
assert!(err.to_string().contains("example.com"));
assert!(err.to_string().contains("Connection refused"));
}
}