openzeppelin_monitor/services/filter/
error.rs

1//! Error types for filter operations.
2//!
3//! Defines the error cases that can occur during block filtering
4//! and provides helper methods for error creation and formatting.
5
6use crate::utils::logging::error::{ErrorContext, TraceableError};
7use std::collections::HashMap;
8use thiserror::Error as ThisError;
9use uuid::Uuid;
10
11/// Represents errors that can occur during filter operations
12#[derive(ThisError, Debug)]
13pub enum FilterError {
14	/// Errors related to network connectivity issues
15	#[error("Block type mismatch error: {0}")]
16	BlockTypeMismatch(ErrorContext),
17
18	/// Errors related to malformed requests or invalid responses
19	#[error("Network error: {0}")]
20	NetworkError(ErrorContext),
21
22	/// Errors related to internal processing errors
23	#[error("Internal error: {0}")]
24	InternalError(ErrorContext),
25
26	/// Other errors that don't fit into the categories above
27	#[error(transparent)]
28	Other(#[from] anyhow::Error),
29}
30
31impl FilterError {
32	// Block type mismatch error
33	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	// Network error
42	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	// Internal error
51	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		// Just test the string representation instead of the source chain
143		assert!(outer_error.to_string().contains("Failed to initialize"));
144
145		// For FilterError::BlockTypeMismatch, we know the implementation details
146		if let FilterError::BlockTypeMismatch(ctx) = &outer_error {
147			// Check that the context has the right message
148			assert_eq!(ctx.message, "Failed to initialize");
149
150			// Check that the context has the source error
151			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		// Create an error context with a known trace ID
164		let error_context = ErrorContext::new("Inner error", None, None);
165		let original_trace_id = error_context.trace_id.clone();
166
167		// Wrap it in a FilterError
168		let filter_error = FilterError::BlockTypeMismatch(error_context);
169
170		// Verify the trace ID is preserved
171		assert_eq!(filter_error.trace_id(), original_trace_id);
172
173		// Test trace ID propagation through error chain
174		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		// Test Other variant
182		let anyhow_error = anyhow::anyhow!("Test anyhow error");
183		let filter_error: FilterError = anyhow_error.into();
184
185		// Other variant should generate a new UUID
186		assert!(!filter_error.trace_id().is_empty());
187	}
188}