Skip to main content

ferro_queue/
error.rs

1//! Error types for the queue system.
2
3use thiserror::Error;
4
5/// Errors that can occur in the queue system.
6#[derive(Debug, Error)]
7pub enum Error {
8    /// Failed to connect to the queue backend.
9    #[error("Queue connection failed: {0}")]
10    ConnectionFailed(String),
11
12    /// Failed to serialize a job.
13    #[error("Failed to serialize job: {0}")]
14    SerializationFailed(String),
15
16    /// Failed to deserialize a job.
17    #[error("Failed to deserialize job: {0}")]
18    DeserializationFailed(String),
19
20    /// Failed to push a job to the queue.
21    #[error("Failed to push job to queue '{queue}': {message}")]
22    PushFailed {
23        /// The queue name.
24        queue: String,
25        /// The error message.
26        message: String,
27    },
28
29    /// Failed to pop a job from the queue.
30    #[error("Failed to pop job from queue '{queue}': {message}")]
31    PopFailed {
32        /// The queue name.
33        queue: String,
34        /// The error message.
35        message: String,
36    },
37
38    /// Job execution failed.
39    #[error("Job '{job}' failed: {message}")]
40    JobFailed {
41        /// The job name.
42        job: String,
43        /// The error message.
44        message: String,
45    },
46
47    /// Maximum retry attempts exceeded.
48    #[error("Job '{job}' exceeded maximum retries ({max_retries})")]
49    MaxRetriesExceeded {
50        /// The job name.
51        job: String,
52        /// Maximum retry count.
53        max_retries: u32,
54    },
55
56    /// Redis error.
57    #[error("Redis error: {0}")]
58    Redis(#[from] redis::RedisError),
59
60    /// JSON error.
61    #[error("JSON error: {0}")]
62    Json(#[from] serde_json::Error),
63
64    /// Tenant not found when trying to establish tenant scope for a job.
65    #[error("Tenant not found for job: tenant_id={tenant_id}")]
66    TenantNotFound {
67        /// The tenant ID that could not be resolved.
68        tenant_id: i64,
69    },
70
71    /// Custom error.
72    #[error("{0}")]
73    Custom(String),
74}
75
76impl Error {
77    /// Create a job failed error.
78    pub fn job_failed(job: impl Into<String>, message: impl Into<String>) -> Self {
79        Self::JobFailed {
80            job: job.into(),
81            message: message.into(),
82        }
83    }
84
85    /// Create a push failed error.
86    pub fn push_failed(queue: impl Into<String>, message: impl Into<String>) -> Self {
87        Self::PushFailed {
88            queue: queue.into(),
89            message: message.into(),
90        }
91    }
92
93    /// Create a tenant not found error.
94    pub fn tenant_not_found(id: i64) -> Self {
95        Self::TenantNotFound { tenant_id: id }
96    }
97
98    /// Create a custom error.
99    pub fn custom(message: impl Into<String>) -> Self {
100        Self::Custom(message.into())
101    }
102}
103
104impl From<String> for Error {
105    fn from(s: String) -> Self {
106        Self::Custom(s)
107    }
108}
109
110impl From<&str> for Error {
111    fn from(s: &str) -> Self {
112        Self::Custom(s.to_string())
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_tenant_not_found_formats_with_id() {
122        let err = Error::tenant_not_found(42);
123        assert_eq!(err.to_string(), "Tenant not found for job: tenant_id=42");
124    }
125
126    #[test]
127    fn test_tenant_not_found_constructor() {
128        let err = Error::tenant_not_found(99);
129        assert!(matches!(err, Error::TenantNotFound { tenant_id: 99 }));
130    }
131}