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(transparent)]
36 Other(#[from] anyhow::Error),
37}
38
39impl BlockWatcherError {
40 pub fn scheduler_error(
42 msg: impl Into<String>,
43 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
44 metadata: Option<HashMap<String, String>>,
45 ) -> Self {
46 Self::SchedulerError(ErrorContext::new_with_log(msg, source, metadata))
47 }
48
49 pub fn network_error(
51 msg: impl Into<String>,
52 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
53 metadata: Option<HashMap<String, String>>,
54 ) -> Self {
55 Self::NetworkError(ErrorContext::new_with_log(msg, source, metadata))
56 }
57
58 pub fn processing_error(
60 msg: impl Into<String>,
61 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
62 metadata: Option<HashMap<String, String>>,
63 ) -> Self {
64 Self::ProcessingError(ErrorContext::new_with_log(msg, source, metadata))
65 }
66
67 pub fn storage_error(
69 msg: impl Into<String>,
70 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
71 metadata: Option<HashMap<String, String>>,
72 ) -> Self {
73 Self::StorageError(ErrorContext::new_with_log(msg, source, metadata))
74 }
75
76 pub fn block_tracker_error(
78 msg: impl Into<String>,
79 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
80 metadata: Option<HashMap<String, String>>,
81 ) -> Self {
82 Self::BlockTrackerError(ErrorContext::new_with_log(msg, source, metadata))
83 }
84}
85
86impl TraceableError for BlockWatcherError {
87 fn trace_id(&self) -> String {
88 match self {
89 Self::SchedulerError(ctx) => ctx.trace_id.clone(),
90 Self::NetworkError(ctx) => ctx.trace_id.clone(),
91 Self::ProcessingError(ctx) => ctx.trace_id.clone(),
92 Self::StorageError(ctx) => ctx.trace_id.clone(),
93 Self::BlockTrackerError(ctx) => ctx.trace_id.clone(),
94 Self::Other(_) => Uuid::new_v4().to_string(),
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use std::io::{Error as IoError, ErrorKind};
103
104 #[test]
105 fn test_scheduler_error_formatting() {
106 let error = BlockWatcherError::scheduler_error("test error", None, None);
107 assert_eq!(error.to_string(), "Scheduler error: test error");
108
109 let source_error = IoError::new(ErrorKind::NotFound, "test source");
110 let error = BlockWatcherError::scheduler_error(
111 "test error",
112 Some(Box::new(source_error)),
113 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
114 );
115 assert_eq!(
116 error.to_string(),
117 "Scheduler error: test error [key1=value1]"
118 );
119 }
120
121 #[test]
122 fn test_network_error_formatting() {
123 let error = BlockWatcherError::network_error("test error", None, None);
124 assert_eq!(error.to_string(), "Network error: test error");
125
126 let source_error = IoError::new(ErrorKind::NotFound, "test source");
127 let error = BlockWatcherError::network_error(
128 "test error",
129 Some(Box::new(source_error)),
130 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
131 );
132 assert_eq!(error.to_string(), "Network error: test error [key1=value1]");
133 }
134
135 #[test]
136 fn test_processing_error_formatting() {
137 let error = BlockWatcherError::processing_error("test error", None, None);
138 assert_eq!(error.to_string(), "Processing error: test error");
139
140 let source_error = IoError::new(ErrorKind::NotFound, "test source");
141 let error = BlockWatcherError::processing_error(
142 "test error",
143 Some(Box::new(source_error)),
144 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
145 );
146 assert_eq!(
147 error.to_string(),
148 "Processing error: test error [key1=value1]"
149 );
150 }
151
152 #[test]
153 fn test_storage_error_formatting() {
154 let error = BlockWatcherError::storage_error("test error", None, None);
155 assert_eq!(error.to_string(), "Storage error: test error");
156
157 let source_error = IoError::new(ErrorKind::NotFound, "test source");
158 let error = BlockWatcherError::storage_error(
159 "test error",
160 Some(Box::new(source_error)),
161 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
162 );
163 assert_eq!(error.to_string(), "Storage error: test error [key1=value1]");
164 }
165
166 #[test]
167 fn test_block_tracker_error_formatting() {
168 let error = BlockWatcherError::block_tracker_error("test error", None, None);
169 assert_eq!(error.to_string(), "Block tracker error: test error");
170
171 let source_error = IoError::new(ErrorKind::NotFound, "test source");
172 let error = BlockWatcherError::block_tracker_error(
173 "test error",
174 Some(Box::new(source_error)),
175 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
176 );
177 assert_eq!(
178 error.to_string(),
179 "Block tracker error: test error [key1=value1]"
180 );
181 }
182
183 #[test]
184 fn test_from_anyhow_error() {
185 let anyhow_error = anyhow::anyhow!("test anyhow error");
186 let block_watcher_error: BlockWatcherError = anyhow_error.into();
187 assert!(matches!(block_watcher_error, BlockWatcherError::Other(_)));
188 assert_eq!(block_watcher_error.to_string(), "test anyhow error");
189 }
190
191 #[test]
192 fn test_error_source_chain() {
193 let io_error = std::io::Error::new(std::io::ErrorKind::Other, "while reading config");
194
195 let outer_error = BlockWatcherError::scheduler_error(
196 "Failed to initialize",
197 Some(Box::new(io_error)),
198 None,
199 );
200
201 assert!(outer_error.to_string().contains("Failed to initialize"));
203
204 if let BlockWatcherError::SchedulerError(ctx) = &outer_error {
206 assert_eq!(ctx.message, "Failed to initialize");
208
209 assert!(ctx.source.is_some());
211
212 if let Some(src) = &ctx.source {
213 assert_eq!(src.to_string(), "while reading config");
214 }
215 } else {
216 panic!("Expected SchedulerError variant");
217 }
218 }
219
220 #[test]
221 fn test_trace_id_propagation() {
222 let error_context = ErrorContext::new("Inner error", None, None);
224 let original_trace_id = error_context.trace_id.clone();
225
226 let block_watcher_error = BlockWatcherError::SchedulerError(error_context);
228
229 assert_eq!(block_watcher_error.trace_id(), original_trace_id);
231
232 let source_error = IoError::new(ErrorKind::Other, "Source error");
234 let error_context = ErrorContext::new("Middle error", Some(Box::new(source_error)), None);
235 let original_trace_id = error_context.trace_id.clone();
236
237 let block_watcher_error = BlockWatcherError::SchedulerError(error_context);
238 assert_eq!(block_watcher_error.trace_id(), original_trace_id);
239
240 let anyhow_error = anyhow::anyhow!("Test anyhow error");
242 let block_watcher_error: BlockWatcherError = anyhow_error.into();
243
244 assert!(!block_watcher_error.trace_id().is_empty());
246 }
247}