openzeppelin_monitor/services/filter/
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 FilterError {
14 #[error("Block type mismatch error: {0}")]
16 BlockTypeMismatch(ErrorContext),
17
18 #[error("Network error: {0}")]
20 NetworkError(ErrorContext),
21
22 #[error("Internal error: {0}")]
24 InternalError(ErrorContext),
25
26 #[error(transparent)]
28 Other(#[from] anyhow::Error),
29}
30
31impl FilterError {
32 pub fn block_type_mismatch(
34 msg: impl Into<String>,
35 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
36 metadata: Option<HashMap<String, String>>,
37 ) -> Self {
38 Self::BlockTypeMismatch(ErrorContext::new_with_log(msg, source, metadata))
39 }
40
41 pub fn network_error(
43 msg: impl Into<String>,
44 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
45 metadata: Option<HashMap<String, String>>,
46 ) -> Self {
47 Self::NetworkError(ErrorContext::new_with_log(msg, source, metadata))
48 }
49
50 pub fn internal_error(
52 msg: impl Into<String>,
53 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
54 metadata: Option<HashMap<String, String>>,
55 ) -> Self {
56 Self::InternalError(ErrorContext::new_with_log(msg, source, metadata))
57 }
58}
59
60impl TraceableError for FilterError {
61 fn trace_id(&self) -> String {
62 match self {
63 Self::BlockTypeMismatch(ctx) => ctx.trace_id.clone(),
64 Self::NetworkError(ctx) => ctx.trace_id.clone(),
65 Self::InternalError(ctx) => ctx.trace_id.clone(),
66 Self::Other(_) => Uuid::new_v4().to_string(),
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use std::io::{Error as IoError, ErrorKind};
75
76 #[test]
77 fn test_block_type_mismatch_error_formatting() {
78 let error = FilterError::block_type_mismatch("test error", None, None);
79 assert_eq!(error.to_string(), "Block type mismatch error: test error");
80
81 let source_error = IoError::new(ErrorKind::NotFound, "test source");
82 let error = FilterError::block_type_mismatch(
83 "test error",
84 Some(Box::new(source_error)),
85 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
86 );
87 assert_eq!(
88 error.to_string(),
89 "Block type mismatch error: test error [key1=value1]"
90 );
91 }
92
93 #[test]
94 fn test_network_error_formatting() {
95 let error = FilterError::network_error("test error", None, None);
96 assert_eq!(error.to_string(), "Network error: test error");
97
98 let source_error = IoError::new(ErrorKind::NotFound, "test source");
99 let error = FilterError::network_error(
100 "test error",
101 Some(Box::new(source_error)),
102 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
103 );
104 assert_eq!(error.to_string(), "Network error: test error [key1=value1]");
105 }
106
107 #[test]
108 fn test_internal_error_formatting() {
109 let error = FilterError::internal_error("test error", None, None);
110 assert_eq!(error.to_string(), "Internal error: test error");
111
112 let source_error = IoError::new(ErrorKind::NotFound, "test source");
113 let error = FilterError::internal_error(
114 "test error",
115 Some(Box::new(source_error)),
116 Some(HashMap::from([("key1".to_string(), "value1".to_string())])),
117 );
118 assert_eq!(
119 error.to_string(),
120 "Internal error: test error [key1=value1]"
121 );
122 }
123
124 #[test]
125 fn test_from_anyhow_error() {
126 let anyhow_error = anyhow::anyhow!("test anyhow error");
127 let filter_error: FilterError = anyhow_error.into();
128 assert!(matches!(filter_error, FilterError::Other(_)));
129 assert_eq!(filter_error.to_string(), "test anyhow error");
130 }
131
132 #[test]
133 fn test_error_source_chain() {
134 let io_error = std::io::Error::new(std::io::ErrorKind::Other, "while reading config");
135
136 let outer_error = FilterError::block_type_mismatch(
137 "Failed to initialize",
138 Some(Box::new(io_error)),
139 None,
140 );
141
142 assert!(outer_error.to_string().contains("Failed to initialize"));
144
145 if let FilterError::BlockTypeMismatch(ctx) = &outer_error {
147 assert_eq!(ctx.message, "Failed to initialize");
149
150 assert!(ctx.source.is_some());
152
153 if let Some(src) = &ctx.source {
154 assert_eq!(src.to_string(), "while reading config");
155 }
156 } else {
157 panic!("Expected BlockTypeMismatch variant");
158 }
159 }
160
161 #[test]
162 fn test_trace_id_propagation() {
163 let error_context = ErrorContext::new("Inner error", None, None);
165 let original_trace_id = error_context.trace_id.clone();
166
167 let filter_error = FilterError::BlockTypeMismatch(error_context);
169
170 assert_eq!(filter_error.trace_id(), original_trace_id);
172
173 let source_error = IoError::new(ErrorKind::Other, "Source error");
175 let error_context = ErrorContext::new("Middle error", Some(Box::new(source_error)), None);
176 let original_trace_id = error_context.trace_id.clone();
177
178 let filter_error = FilterError::BlockTypeMismatch(error_context);
179 assert_eq!(filter_error.trace_id(), original_trace_id);
180
181 let anyhow_error = anyhow::anyhow!("Test anyhow error");
183 let filter_error: FilterError = anyhow_error.into();
184
185 assert!(!filter_error.trace_id().is_empty());
187 }
188}