use thiserror::Error;
pub type SearchResult<T> = std::result::Result<T, SearchError>;
#[derive(Error, Debug, Clone)]
pub enum SearchError {
#[error("HTTP request failed: {message}")]
HttpError {
message: String,
status_code: Option<u16>,
response_body: Option<String>,
},
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Provider error: {0}")]
ProviderError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Parsing error: {0}")]
ParseError(String),
#[error("Request timed out after {timeout_ms}ms")]
Timeout { timeout_ms: u64 },
#[error("Rate limit exceeded: {0}")]
RateLimit(String),
#[error("Authentication failed: {0}")]
AuthenticationError(String),
#[error("Search error: {0}")]
Other(String),
}
impl From<reqwest::Error> for SearchError {
fn from(error: reqwest::Error) -> Self {
if error.is_timeout() {
SearchError::Timeout {
timeout_ms: 15000, }
} else if error.is_status() {
let status_code = error.status().map(|s| s.as_u16());
let message = error.to_string();
if let Some(401 | 403) = status_code {
SearchError::AuthenticationError(message)
} else if let Some(429) = status_code {
SearchError::RateLimit(message)
} else {
SearchError::HttpError {
message,
status_code,
response_body: None,
}
}
} else {
SearchError::HttpError {
message: error.to_string(),
status_code: None,
response_body: None,
}
}
}
}
impl From<serde_json::Error> for SearchError {
fn from(error: serde_json::Error) -> Self {
SearchError::ParseError(format!("JSON parsing failed: {error}"))
}
}
impl From<url::ParseError> for SearchError {
fn from(error: url::ParseError) -> Self {
SearchError::InvalidInput(format!("Invalid URL: {error}"))
}
}
impl From<std::io::Error> for SearchError {
fn from(error: std::io::Error) -> Self {
SearchError::Other(format!("IO error: {error}"))
}
}