1use std::io;
7
8#[derive(Debug, thiserror::Error)]
10pub enum AdvisoryError {
11 #[error("Redis error: {0}")]
13 Redis(#[from] redis::RedisError),
14
15 #[error("Source '{source_name}' fetch failed: {message}")]
17 SourceFetch {
18 source_name: String,
20 message: String,
22 },
23
24 #[error("Configuration error: {0}")]
26 Config(String),
27
28 #[error("Serialization error: {0}")]
30 Serialization(#[from] serde_json::Error),
31
32 #[error("Compression error: {0}")]
34 Compression(String),
35
36 #[error("HTTP error: {0}")]
38 Http(#[from] reqwest::Error),
39
40 #[error("HTTP middleware error: {0}")]
42 HttpMiddleware(#[from] reqwest_middleware::Error),
43
44 #[error("Rate limit exceeded for source '{source_name}': {message}")]
46 RateLimit {
47 source_name: String,
49 message: String,
51 },
52
53 #[error("I/O error: {0}")]
55 Io(io::Error),
56
57 #[error("Invalid version '{version}': {message}")]
59 VersionParse {
60 version: String,
62 message: String,
64 },
65
66 #[error("ZIP error: {0}")]
68 Zip(#[from] zip::result::ZipError),
69
70 #[error("Task join error: {0}")]
72 TaskJoin(#[from] tokio::task::JoinError),
73
74 #[error("GraphQL error: {0}")]
76 GraphQL(String),
77}
78
79pub type Result<T> = std::result::Result<T, AdvisoryError>;
81
82impl AdvisoryError {
83 pub fn source_fetch(source: impl Into<String>, message: impl Into<String>) -> Self {
85 Self::SourceFetch {
86 source_name: source.into(),
87 message: message.into(),
88 }
89 }
90
91 pub fn config(message: impl Into<String>) -> Self {
93 Self::Config(message.into())
94 }
95
96 pub fn compression(message: impl Into<String>) -> Self {
98 Self::Compression(message.into())
99 }
100
101 pub fn rate_limit(source: impl Into<String>, message: impl Into<String>) -> Self {
103 Self::RateLimit {
104 source_name: source.into(),
105 message: message.into(),
106 }
107 }
108
109 pub fn version_parse(version: impl Into<String>, message: impl Into<String>) -> Self {
111 Self::VersionParse {
112 version: version.into(),
113 message: message.into(),
114 }
115 }
116
117 pub fn graphql(message: impl Into<String>) -> Self {
119 Self::GraphQL(message.into())
120 }
121
122 pub fn is_retryable(&self) -> bool {
124 matches!(
125 self,
126 Self::Http(_) | Self::HttpMiddleware(_) | Self::RateLimit { .. } | Self::Redis(_)
127 )
128 }
129}
130
131impl From<std::io::Error> for AdvisoryError {
133 fn from(err: std::io::Error) -> Self {
134 if err.to_string().contains("zstd") || err.to_string().contains("compress") {
136 Self::Compression(err.to_string())
137 } else {
138 Self::Io(err)
139 }
140 }
141}