use thiserror::Error;
#[derive(Debug, Error)]
pub enum PubSubError {
#[error("gRPC transport error: {0}")]
Transport(#[from] tonic::Status),
#[error("Avro error: {0}")]
Avro(String),
#[error("schema not found: {schema_id}")]
SchemaNotFound {
schema_id: String,
},
#[error("auth error: {0}")]
Auth(#[from] force::error::ForceError),
#[error("reconnect failed after {attempts} attempt(s): {last_error}")]
ReconnectFailed {
attempts: u32,
last_error: Box<Self>,
},
#[error("failed to connect to Pub/Sub endpoint: {0}")]
Connect(#[from] tonic::transport::Error),
#[error("invalid configuration: {0}")]
Config(String),
}
pub type Result<T> = std::result::Result<T, PubSubError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_schema_not_found_display() {
let err = PubSubError::SchemaNotFound {
schema_id: "abc123".to_string(),
};
assert_eq!(err.to_string(), "schema not found: abc123");
}
#[test]
fn test_config_error_display() {
let err = PubSubError::Config("batch_size must be > 0".to_string());
assert_eq!(
err.to_string(),
"invalid configuration: batch_size must be > 0"
);
}
#[test]
fn test_avro_error_display() {
let err = PubSubError::Avro("unexpected end of buffer".to_string());
assert_eq!(err.to_string(), "Avro error: unexpected end of buffer");
}
#[test]
fn test_reconnect_failed_display() {
let inner = Box::new(PubSubError::Config("test".to_string()));
let err = PubSubError::ReconnectFailed {
attempts: 3,
last_error: inner,
};
assert!(err.to_string().contains("3 attempt(s)"));
}
#[test]
fn test_from_tonic_status() {
let status = tonic::Status::not_found("topic not found");
let err = PubSubError::from(status);
assert!(matches!(err, PubSubError::Transport(_)));
}
}