use std::time::Duration;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum WorkerError {
#[error("Message processing failed: {0}")]
ProcessingFailed(String),
#[error("Processing error: {0}")]
ProcessingError(String),
#[error("Backend error: {0}")]
BackendError(String),
#[error("Acknowledgment failed: {0}")]
AcknowledgmentFailed(String),
#[error("Worker {id} panicked: {panic_info}")]
WorkerPanic { id: String, panic_info: String },
#[error("Pool exhausted: no available workers")]
PoolExhausted,
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Shutdown requested")]
Shutdown,
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("Timeout error: {0}")]
Timeout(String),
#[error("Channel error: {0}")]
ChannelError(String),
#[error("Retryable failure: {source}. Retrying in {delay_ms:?}ms")]
RetryableFailure {
source: Box<WorkerError>,
delay_ms: Duration,
},
#[error("Retries exhausted for message: {source}")]
RetriesExhausted { source: Box<WorkerError> },
#[error("Message already acknowledged by middleware")]
AlreadyAcknowledged,
#[error("Application error: {0}")]
AppError(#[from] anyhow::Error),
}
pub type WorkerResult<T> = Result<T, WorkerError>;
impl From<std::io::Error> for WorkerError {
fn from(err: std::io::Error) -> Self {
WorkerError::BackendError(err.to_string())
}
}
impl From<tokio::task::JoinError> for WorkerError {
fn from(err: tokio::task::JoinError) -> Self {
match err.try_into_panic() {
Ok(reason) => {
if let Some(s) = reason.downcast_ref::<String>() {
WorkerError::ProcessingFailed(format!("Task panicked: {}", s))
} else if let Some(s) = reason.downcast_ref::<&str>() {
WorkerError::ProcessingFailed(format!("Task panicked: {}", s))
} else {
WorkerError::ProcessingFailed("Task panicked with unknown reason".to_string())
}
}
Err(cancelled_err) => {
WorkerError::ProcessingFailed(format!("Task cancelled: {}", cancelled_err))
}
}
}
}