use crate::message::Timestamp;
use chrono::Duration;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum QueueError {
#[error("Queue not found: {queue_name}")]
QueueNotFound { queue_name: String },
#[error("Message not found: {receipt}")]
MessageNotFound { receipt: String },
#[error("Invalid or expired receipt handle: {receipt}")]
InvalidReceipt { receipt: String },
#[error("Session '{session_id}' is locked until {locked_until}")]
SessionLocked {
session_id: String,
locked_until: Timestamp,
},
#[error("Session '{session_id}' not found or expired")]
SessionNotFound { session_id: String },
#[error("Operation timed out after {duration:?}")]
Timeout { duration: Duration },
#[error("Connection failed: {message}")]
ConnectionFailed { message: String },
#[error("Authentication failed: {message}")]
AuthenticationFailed { message: String },
#[error("Permission denied for operation: {operation}")]
PermissionDenied { operation: String },
#[error("Message too large: {size} bytes (max: {max_size})")]
MessageTooLarge { size: usize, max_size: usize },
#[error("Batch size {size} exceeds maximum {max_size}")]
BatchTooLarge { size: usize, max_size: usize },
#[error("Provider error ({provider}): {code} - {message}")]
ProviderError {
provider: String,
code: String,
message: String,
},
#[error("Serialization failed: {0}")]
SerializationError(#[from] SerializationError),
#[error("Configuration error: {0}")]
ConfigurationError(#[from] ConfigurationError),
#[error("Validation error: {0}")]
ValidationError(#[from] ValidationError),
}
impl QueueError {
pub fn is_transient(&self) -> bool {
match self {
Self::QueueNotFound { .. } => false,
Self::MessageNotFound { .. } => false,
Self::InvalidReceipt { .. } => false,
Self::SessionLocked { .. } => true,
Self::SessionNotFound { .. } => false,
Self::Timeout { .. } => true,
Self::ConnectionFailed { .. } => true,
Self::AuthenticationFailed { .. } => false,
Self::PermissionDenied { .. } => false,
Self::MessageTooLarge { .. } => false,
Self::BatchTooLarge { .. } => false,
Self::ProviderError { .. } => true, Self::SerializationError(_) => false,
Self::ConfigurationError(_) => false,
Self::ValidationError(_) => false,
}
}
pub fn should_retry(&self) -> bool {
self.is_transient()
}
pub fn retry_after(&self) -> Option<Duration> {
match self {
Self::SessionLocked { .. } => Some(Duration::seconds(5)),
Self::Timeout { .. } => Some(Duration::seconds(1)),
Self::ConnectionFailed { .. } => Some(Duration::seconds(5)),
_ => None,
}
}
}
#[derive(Debug, Error)]
pub enum SerializationError {
#[error("JSON serialization failed: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Message body is not valid UTF-8")]
InvalidUtf8,
#[error("Message attribute '{key}' has invalid value")]
InvalidAttribute { key: String },
#[error("Message exceeds size limit: {size} bytes")]
MessageTooLarge { size: usize },
}
#[derive(Debug, Error)]
pub enum ConfigurationError {
#[error("Invalid configuration: {message}")]
Invalid { message: String },
#[error("Missing required configuration: {key}")]
Missing { key: String },
#[error("Configuration parsing failed: {message}")]
Parsing { message: String },
#[error("Unsupported provider: {provider} - {message}")]
UnsupportedProvider { provider: String, message: String },
}
#[derive(Debug, Error)]
pub enum ValidationError {
#[error("Required field missing: {field}")]
Required { field: String },
#[error("Invalid format for {field}: {message}")]
InvalidFormat { field: String, message: String },
#[error("Value out of range for {field}: {message}")]
OutOfRange { field: String, message: String },
}
#[cfg(test)]
#[path = "error_tests.rs"]
mod tests;