1use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum PubSubError {
12 #[error("gRPC transport error: {0}")]
14 Transport(#[from] tonic::Status),
15
16 #[error("Avro error: {0}")]
18 Avro(String),
19
20 #[error("schema not found: {schema_id}")]
22 SchemaNotFound {
23 schema_id: String,
25 },
26
27 #[error("auth error: {0}")]
29 Auth(#[from] force::error::ForceError),
30
31 #[error("reconnect failed after {attempts} attempt(s): {last_error}")]
33 ReconnectFailed {
34 attempts: u32,
36 last_error: Box<Self>,
38 },
39
40 #[error("failed to connect to Pub/Sub endpoint: {0}")]
42 Connect(#[from] tonic::transport::Error),
43
44 #[error("invalid configuration: {0}")]
46 Config(String),
47}
48
49pub type Result<T> = std::result::Result<T, PubSubError>;
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn test_schema_not_found_display() {
58 let err = PubSubError::SchemaNotFound {
59 schema_id: "abc123".to_string(),
60 };
61 assert_eq!(err.to_string(), "schema not found: abc123");
62 }
63
64 #[test]
65 fn test_config_error_display() {
66 let err = PubSubError::Config("batch_size must be > 0".to_string());
67 assert_eq!(
68 err.to_string(),
69 "invalid configuration: batch_size must be > 0"
70 );
71 }
72
73 #[test]
74 fn test_avro_error_display() {
75 let err = PubSubError::Avro("unexpected end of buffer".to_string());
76 assert_eq!(err.to_string(), "Avro error: unexpected end of buffer");
77 }
78
79 #[test]
80 fn test_reconnect_failed_display() {
81 let inner = Box::new(PubSubError::Config("test".to_string()));
82 let err = PubSubError::ReconnectFailed {
83 attempts: 3,
84 last_error: inner,
85 };
86 assert!(err.to_string().contains("3 attempt(s)"));
87 }
88
89 #[test]
90 fn test_from_tonic_status() {
91 let status = tonic::Status::not_found("topic not found");
92 let err = PubSubError::from(status);
93 assert!(matches!(err, PubSubError::Transport(_)));
94 }
95}