openzeppelin_monitor/services/blockwatcher/
error.rs1use crate::utils::logging::error::{ErrorContext, TraceableError};
7use std::collections::HashMap;
8use thiserror::Error as ThisError;
9use uuid::Uuid;
10
11#[derive(ThisError, Debug)]
13pub enum BlockWatcherError {
14 #[error("Scheduler error: {0}")]
16 SchedulerError(ErrorContext),
17
18 #[error("Network error: {0}")]
20 NetworkError(ErrorContext),
21
22 #[error("Processing error: {0}")]
24 ProcessingError(ErrorContext),
25
26 #[error("Storage error: {0}")]
28 StorageError(ErrorContext),
29
30 #[error("Block tracker error: {0}")]
32 BlockTrackerError(ErrorContext),
33
34 #[error("Recovery error: {0}")]
36 RecoveryError(ErrorContext),
37
38 #[error(transparent)]
40 Other(#[from] anyhow::Error),
41}
42
43impl BlockWatcherError {
44 pub fn scheduler_error(
46 msg: impl Into<String>,
47 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
48 metadata: Option<HashMap<String, String>>,
49 ) -> Self {
50 Self::SchedulerError(ErrorContext::new_with_log(msg, source, metadata))
51 }
52
53 pub fn network_error(
55 msg: impl Into<String>,
56 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
57 metadata: Option<HashMap<String, String>>,
58 ) -> Self {
59 Self::NetworkError(ErrorContext::new_with_log(msg, source, metadata))
60 }
61
62 pub fn processing_error(
64 msg: impl Into<String>,
65 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
66 metadata: Option<HashMap<String, String>>,
67 ) -> Self {
68 Self::ProcessingError(ErrorContext::new_with_log(msg, source, metadata))
69 }
70
71 pub fn storage_error(
73 msg: impl Into<String>,
74 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
75 metadata: Option<HashMap<String, String>>,
76 ) -> Self {
77 Self::StorageError(ErrorContext::new_with_log(msg, source, metadata))
78 }
79
80 pub fn block_tracker_error(
82 msg: impl Into<String>,
83 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
84 metadata: Option<HashMap<String, String>>,
85 ) -> Self {
86 Self::BlockTrackerError(ErrorContext::new_with_log(msg, source, metadata))
87 }
88
89 pub fn recovery_error(
91 msg: impl Into<String>,
92 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
93 metadata: Option<HashMap<String, String>>,
94 ) -> Self {
95 Self::RecoveryError(ErrorContext::new_with_log(msg, source, metadata))
96 }
97}
98
99impl TraceableError for BlockWatcherError {
100 fn trace_id(&self) -> String {
101 match self {
102 Self::SchedulerError(ctx) => ctx.trace_id.clone(),
103 Self::NetworkError(ctx) => ctx.trace_id.clone(),
104 Self::ProcessingError(ctx) => ctx.trace_id.clone(),
105 Self::StorageError(ctx) => ctx.trace_id.clone(),
106 Self::BlockTrackerError(ctx) => ctx.trace_id.clone(),
107 Self::RecoveryError(ctx) => ctx.trace_id.clone(),
108 Self::Other(_) => Uuid::new_v4().to_string(),
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use std::io::{Error as IoError, ErrorKind};
117
118 #[test]
119 fn test_scheduler_error_formatting() {
120 let error = BlockWatcherError::scheduler_error("test error", None, None);
121 assert_eq!(error.to_string(), "Scheduler error: test error");
122
123 let source_error = IoError::new(ErrorKind::NotFound, "test source");
124 let error = BlockWatcherError::scheduler_error(
125 "test error",
126 Some(Box::new(source_error)),
127 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
128 );
129 assert_eq!(
130 error.to_string(),
131 "Scheduler error: test error [key1=value1]"
132 );
133 }
134
135 #[test]
136 fn test_network_error_formatting() {
137 let error = BlockWatcherError::network_error("test error", None, None);
138 assert_eq!(error.to_string(), "Network error: test error");
139
140 let source_error = IoError::new(ErrorKind::NotFound, "test source");
141 let error = BlockWatcherError::network_error(
142 "test error",
143 Some(Box::new(source_error)),
144 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
145 );
146 assert_eq!(error.to_string(), "Network error: test error [key1=value1]");
147 }
148
149 #[test]
150 fn test_processing_error_formatting() {
151 let error = BlockWatcherError::processing_error("test error", None, None);
152 assert_eq!(error.to_string(), "Processing error: test error");
153
154 let source_error = IoError::new(ErrorKind::NotFound, "test source");
155 let error = BlockWatcherError::processing_error(
156 "test error",
157 Some(Box::new(source_error)),
158 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
159 );
160 assert_eq!(
161 error.to_string(),
162 "Processing error: test error [key1=value1]"
163 );
164 }
165
166 #[test]
167 fn test_storage_error_formatting() {
168 let error = BlockWatcherError::storage_error("test error", None, None);
169 assert_eq!(error.to_string(), "Storage error: test error");
170
171 let source_error = IoError::new(ErrorKind::NotFound, "test source");
172 let error = BlockWatcherError::storage_error(
173 "test error",
174 Some(Box::new(source_error)),
175 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
176 );
177 assert_eq!(error.to_string(), "Storage error: test error [key1=value1]");
178 }
179
180 #[test]
181 fn test_block_tracker_error_formatting() {
182 let error = BlockWatcherError::block_tracker_error("test error", None, None);
183 assert_eq!(error.to_string(), "Block tracker error: test error");
184
185 let source_error = IoError::new(ErrorKind::NotFound, "test source");
186 let error = BlockWatcherError::block_tracker_error(
187 "test error",
188 Some(Box::new(source_error)),
189 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
190 );
191 assert_eq!(
192 error.to_string(),
193 "Block tracker error: test error [key1=value1]"
194 );
195 }
196
197 #[test]
198 fn test_recovery_error_formatting() {
199 let error = BlockWatcherError::recovery_error("test error", None, None);
200 assert_eq!(error.to_string(), "Recovery error: test error");
201
202 let source_error = IoError::new(ErrorKind::NotFound, "test source");
203 let error = BlockWatcherError::recovery_error(
204 "test error",
205 Some(Box::new(source_error)),
206 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
207 );
208 assert_eq!(
209 error.to_string(),
210 "Recovery error: test error [key1=value1]"
211 );
212
213 let error = BlockWatcherError::recovery_error("trace test", None, None);
215 assert!(!error.trace_id().is_empty());
216 }
217
218 #[test]
219 fn test_from_anyhow_error() {
220 let anyhow_error = anyhow::anyhow!("test anyhow error");
221 let block_watcher_error: BlockWatcherError = anyhow_error.into();
222 assert!(matches!(block_watcher_error, BlockWatcherError::Other(_)));
223 assert_eq!(block_watcher_error.to_string(), "test anyhow error");
224 }
225
226 #[test]
227 fn test_error_source_chain() {
228 let io_error = std::io::Error::other("while reading config");
229
230 let outer_error = BlockWatcherError::scheduler_error(
231 "Failed to initialize",
232 Some(Box::new(io_error)),
233 None,
234 );
235
236 assert!(outer_error.to_string().contains("Failed to initialize"));
238
239 if let BlockWatcherError::SchedulerError(ctx) = &outer_error {
241 assert_eq!(ctx.message, "Failed to initialize");
243
244 assert!(ctx.source.is_some());
246
247 if let Some(src) = &ctx.source {
248 assert_eq!(src.to_string(), "while reading config");
249 }
250 } else {
251 panic!("Expected SchedulerError variant");
252 }
253 }
254
255 #[test]
256 fn test_trace_id_propagation() {
257 let error_context = ErrorContext::new("Inner error", None, None);
259 let original_trace_id = error_context.trace_id.clone();
260
261 let block_watcher_error = BlockWatcherError::SchedulerError(error_context);
263
264 assert_eq!(block_watcher_error.trace_id(), original_trace_id);
266
267 let source_error = IoError::other("Source error");
269 let error_context = ErrorContext::new("Middle error", Some(Box::new(source_error)), None);
270 let original_trace_id = error_context.trace_id.clone();
271
272 let block_watcher_error = BlockWatcherError::SchedulerError(error_context);
273 assert_eq!(block_watcher_error.trace_id(), original_trace_id);
274
275 let anyhow_error = anyhow::anyhow!("Test anyhow error");
277 let block_watcher_error: BlockWatcherError = anyhow_error.into();
278
279 assert!(!block_watcher_error.trace_id().is_empty());
281 }
282}