1use thiserror::Error;
2
3#[derive(Error, Debug)]
5pub enum ProcessingError {
6 #[error("retryable error: {0}")]
8 Retryable(#[source] anyhow::Error),
9 #[error("non-retryable error: {0}")]
11 NonRetryable(#[source] anyhow::Error),
12 #[error("connection error: {0}")]
14 Connection(#[source] anyhow::Error),
15}
16impl ProcessingError {
17 pub fn is_connection_error(&self) -> bool {
18 matches!(self, ProcessingError::Connection(_))
19 }
20}
21
22pub type HandlerError = ProcessingError;
23pub type PublisherError = ProcessingError;
24
25#[derive(Error, Debug)]
27pub enum ConsumerError {
28 #[error("consumer connection error: {0}")]
30 Connection(#[source] anyhow::Error),
31
32 #[error("consumer gap: requested offset {requested} but earliest available is {base}")]
34 Gap { requested: u64, base: u64 },
35
36 #[error("consumer reached end of stream")]
38 EndOfStream,
39
40 #[error("permanent consumer error: {0}")]
43 Permanent(#[source] anyhow::Error),
44}
45
46impl From<anyhow::Error> for ConsumerError {
47 fn from(err: anyhow::Error) -> Self {
48 ConsumerError::Connection(err)
50 }
51}
52
53impl From<anyhow::Error> for ProcessingError {
54 fn from(err: anyhow::Error) -> Self {
55 ProcessingError::Retryable(err)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_is_connection_error_only_matches_connection_variant() {
67 assert!(ProcessingError::Connection(anyhow::anyhow!("offline")).is_connection_error());
68 assert!(!ProcessingError::Retryable(anyhow::anyhow!("retry")).is_connection_error());
69 assert!(!ProcessingError::NonRetryable(anyhow::anyhow!("stop")).is_connection_error());
70 }
71
72 #[test]
73 fn test_anyhow_error_conversions_use_default_variants() {
74 let consumer_error = ConsumerError::from(anyhow::anyhow!("consumer failure"));
75 assert!(matches!(consumer_error, ConsumerError::Connection(_)));
76
77 let processing_error = ProcessingError::from(anyhow::anyhow!("processing failure"));
78 assert!(matches!(processing_error, ProcessingError::Retryable(_)));
79 }
80
81 #[test]
82 fn test_consumer_error_display_messages() {
83 assert_eq!(
84 ConsumerError::Gap {
85 requested: 42,
86 base: 9
87 }
88 .to_string(),
89 "consumer gap: requested offset 42 but earliest available is 9"
90 );
91 assert_eq!(
92 ConsumerError::EndOfStream.to_string(),
93 "consumer reached end of stream"
94 );
95 }
96}