use std::io;
#[derive(Debug, thiserror::Error)]
pub enum AdvisoryError {
#[error("Redis error: {0}")]
Redis(#[from] redis::RedisError),
#[error("Source '{source_name}' fetch failed: {message}")]
SourceFetch {
source_name: String,
message: String,
},
#[error("Configuration error: {0}")]
Config(String),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("Compression error: {0}")]
Compression(String),
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("HTTP middleware error: {0}")]
HttpMiddleware(#[from] reqwest_middleware::Error),
#[error("Rate limit exceeded for source '{source_name}': {message}")]
RateLimit {
source_name: String,
message: String,
},
#[error("I/O error: {0}")]
Io(io::Error),
#[error("Invalid version '{version}': {message}")]
VersionParse {
version: String,
message: String,
},
#[error("ZIP error: {0}")]
Zip(#[from] zip::result::ZipError),
#[error("Task join error: {0}")]
TaskJoin(#[from] tokio::task::JoinError),
#[error("GraphQL error: {0}")]
GraphQL(String),
}
pub type Result<T> = std::result::Result<T, AdvisoryError>;
impl AdvisoryError {
pub fn source_fetch(source: impl Into<String>, message: impl Into<String>) -> Self {
Self::SourceFetch {
source_name: source.into(),
message: message.into(),
}
}
pub fn config(message: impl Into<String>) -> Self {
Self::Config(message.into())
}
pub fn compression(message: impl Into<String>) -> Self {
Self::Compression(message.into())
}
pub fn rate_limit(source: impl Into<String>, message: impl Into<String>) -> Self {
Self::RateLimit {
source_name: source.into(),
message: message.into(),
}
}
pub fn version_parse(version: impl Into<String>, message: impl Into<String>) -> Self {
Self::VersionParse {
version: version.into(),
message: message.into(),
}
}
pub fn graphql(message: impl Into<String>) -> Self {
Self::GraphQL(message.into())
}
pub fn is_retryable(&self) -> bool {
matches!(
self,
Self::Http(_) | Self::HttpMiddleware(_) | Self::RateLimit { .. } | Self::Redis(_)
)
}
}
impl From<std::io::Error> for AdvisoryError {
fn from(err: std::io::Error) -> Self {
if err.to_string().contains("zstd") || err.to_string().contains("compress") {
Self::Compression(err.to_string())
} else {
Self::Io(err)
}
}
}