use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("Redis connection error: {0}")]
Redis(#[from] redis::RedisError),
#[error("Redis parsing error: {0}")]
RedisParsing(#[from] redis::ParsingError),
#[cfg(feature = "postgres")]
#[error("SeaORM database error: {0}")]
SeaOrm(#[from] sea_orm::DbErr),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Deserialization error: {0}")]
Deserialization(String),
#[error("Protocol buffer encoding error: {0}")]
ProtoEncode(#[from] prost::EncodeError),
#[error("Protocol buffer decoding error: {0}")]
ProtoDecode(#[from] prost::DecodeError),
#[error("Task already exists")]
TaskDuplicate,
#[error("Task ID conflicts with another task")]
TaskIdConflict,
#[error("Task not found: {id}")]
TaskNotFound { id: String },
#[error("Queue error: {message}")]
Queue { message: String },
#[error("Invalid queue name: {name}")]
InvalidQueueName { name: String },
#[error("Invalid task type: {task_type}")]
InvalidTaskType { task_type: String },
#[error("Server closed")]
ServerClosed,
#[error("Server is already running")]
ServerRunning,
#[error("Operation timeout")]
Timeout,
#[error("Operation cancelled")]
Cancelled,
#[error("Configuration error: {message}")]
Config { message: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Other error: {message}")]
Other { message: String },
#[error("Not implemented: {0}")]
NotImplemented(String),
#[error("Not supported: {0}")]
NotSupported(String),
#[error("WebSocket error: {0}")]
WebSocket(String),
#[error("Invalid message: {0}")]
InvalidMessage(String),
#[error("Broker error: {0}")]
Broker(String),
#[error(transparent)]
SkipRetry(#[from] SkipRetryError),
}
impl Error {
pub fn queue<S: Into<String>>(message: S) -> Self {
Self::Queue {
message: message.into(),
}
}
pub fn config<S: Into<String>>(message: S) -> Self {
Self::Config {
message: message.into(),
}
}
pub fn other<S: Into<String>>(message: S) -> Self {
Self::Other {
message: message.into(),
}
}
pub fn not_supported<S: Into<String>>(message: S) -> Self {
Self::NotSupported(message.into())
}
pub fn websocket<S: Into<String>>(message: S) -> Self {
Self::WebSocket(message.into())
}
pub fn invalid_message<S: Into<String>>(message: S) -> Self {
Self::InvalidMessage(message.into())
}
pub fn broker<S: Into<String>>(message: S) -> Self {
Self::Broker(message.into())
}
pub fn is_retriable(&self) -> bool {
match self {
Error::Redis(_) => return true,
Error::RedisParsing(_) => {}
Error::ProtoEncode(_) => {}
Error::ProtoDecode(_) => {}
Error::TaskDuplicate => {}
Error::TaskIdConflict => {}
Error::TaskNotFound { .. } => {}
Error::Queue { .. } => {}
Error::InvalidQueueName { .. } => {}
Error::InvalidTaskType { .. } => {}
Error::ServerClosed => {}
Error::ServerRunning => {}
Error::Cancelled => {}
Error::Config { .. } => {}
Error::Io(_) | Error::Timeout | Error::WebSocket(_) => {
return true;
}
Error::Other { .. } => {}
Error::NotImplemented(_) => {}
Error::NotSupported(_) => {}
Error::InvalidMessage(_) => {}
Error::Broker(_) => {}
Error::SkipRetry(_) => {}
#[cfg(feature = "postgres")]
Error::SeaOrm(_) => {}
Error::Serialization(_) | Error::Deserialization(_) => {}
}
false
}
pub fn is_fatal(&self) -> bool {
!self.is_retriable()
}
}
#[derive(Error, Debug)]
#[error("Skip retry: {0}")]
pub struct SkipRetryError(pub Box<dyn std::error::Error + Send + Sync>);
impl SkipRetryError {
pub fn new<E>(error: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self(Box::new(error))
}
}
#[derive(Error, Debug)]
#[error("Revoke task: {0}")]
pub struct RevokeTaskError(pub Box<dyn std::error::Error + Send + Sync>);
impl RevokeTaskError {
pub fn new<E>(error: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self(Box::new(error))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = Error::queue("test queue error");
assert!(matches!(err, Error::Queue { .. }));
let err = Error::config("test config error");
assert!(matches!(err, Error::Config { .. }));
let err = Error::other("test other error");
assert!(matches!(err, Error::Other { .. }));
}
#[test]
fn test_error_retriable() {
assert!(Error::Timeout.is_retriable());
assert!(!Error::TaskDuplicate.is_retriable());
assert!(!Error::ServerClosed.is_retriable());
}
#[test]
fn test_skip_retry_error() {
let inner_err = std::io::Error::other("test error");
let skip_err = SkipRetryError::new(inner_err);
assert!(skip_err.to_string().contains("Skip retry"));
}
#[test]
fn test_revoke_task_error() {
let inner_err = std::io::Error::other("test error");
let revoke_err = RevokeTaskError::new(inner_err);
assert!(revoke_err.to_string().contains("Revoke task"));
}
}