use thiserror::Error;
pub type ConnectorResult<T> = Result<T, ConnectorError>;
#[derive(Error, Debug)]
pub enum ConnectorError {
#[error("Retryable error: {message}")]
Retryable {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Fatal error: {message}")]
Fatal {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Invalid data: {message}")]
InvalidData {
message: String,
payload: Vec<u8>,
},
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Danube error: {0}")]
Danube(#[from] danube_client::errors::DanubeError),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
}
impl ConnectorError {
pub fn is_retryable(&self) -> bool {
matches!(self, ConnectorError::Retryable { .. })
}
pub fn is_fatal(&self) -> bool {
matches!(self, ConnectorError::Fatal { .. })
}
pub fn is_invalid_data(&self) -> bool {
matches!(self, ConnectorError::InvalidData { .. })
}
pub fn retryable(message: impl Into<String>) -> Self {
ConnectorError::Retryable {
message: message.into(),
source: None,
}
}
pub fn retryable_with_source(
message: impl Into<String>,
source: impl std::error::Error + Send + Sync + 'static,
) -> Self {
ConnectorError::Retryable {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn fatal(message: impl Into<String>) -> Self {
ConnectorError::Fatal {
message: message.into(),
source: None,
}
}
pub fn fatal_with_source(
message: impl Into<String>,
source: impl std::error::Error + Send + Sync + 'static,
) -> Self {
ConnectorError::Fatal {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn invalid_data(message: impl Into<String>, payload: Vec<u8>) -> Self {
ConnectorError::InvalidData {
message: message.into(),
payload,
}
}
pub fn config(message: impl Into<String>) -> Self {
ConnectorError::Configuration(message.into())
}
}
impl From<serde_json::Error> for ConnectorError {
fn from(err: serde_json::Error) -> Self {
ConnectorError::Serialization(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_classification() {
let retryable = ConnectorError::retryable("network timeout");
assert!(retryable.is_retryable());
assert!(!retryable.is_fatal());
let fatal = ConnectorError::fatal("auth failed");
assert!(!fatal.is_retryable());
assert!(fatal.is_fatal());
let invalid = ConnectorError::invalid_data("bad json", vec![1, 2, 3]);
assert!(invalid.is_invalid_data());
}
#[test]
fn test_error_display() {
let err = ConnectorError::retryable("test error");
assert_eq!(err.to_string(), "Retryable error: test error");
}
}