Skip to main content

axum_tasks/
types.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::time::Duration;
5
6pub const MAX_QUEUE_SIZE: usize = 10_000;
7pub const MAX_RETRIES: u32 = 3;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum TaskResult {
11    Success,
12    RetryableError(String),
13    PermanentError(String),
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub enum TaskOutput {
18    Success(serde_json::Value),
19    RetryableError(String),
20    PermanentError(String),
21}
22
23impl fmt::Display for TaskResult {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            TaskResult::Success => write!(f, "Success"),
27            TaskResult::RetryableError(err) => write!(f, "Retryable Error: {}", err),
28            TaskResult::PermanentError(err) => write!(f, "Permanent Error: {}", err),
29        }
30    }
31}
32
33impl From<TaskOutput> for TaskResult {
34    fn from(output: TaskOutput) -> Self {
35        match output {
36            TaskOutput::Success(_) => TaskResult::Success,
37            TaskOutput::RetryableError(e) => TaskResult::RetryableError(e),
38            TaskOutput::PermanentError(e) => TaskResult::PermanentError(e),
39        }
40    }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44pub enum TaskStatus {
45    Queued,
46    InProgress,
47    Completed,
48    Failed,
49    Retrying,
50    Cancelled,
51}
52
53impl fmt::Display for TaskStatus {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            TaskStatus::Queued => write!(f, "queued"),
57            TaskStatus::InProgress => write!(f, "in_progress"),
58            TaskStatus::Completed => write!(f, "completed"),
59            TaskStatus::Failed => write!(f, "failed"),
60            TaskStatus::Retrying => write!(f, "retrying"),
61            TaskStatus::Cancelled => write!(f, "cancelled"),
62        }
63    }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct TaskState {
68    pub id: String,
69    pub task_name: String,
70    pub task_data: serde_json::Value,
71    pub status: TaskStatus,
72    pub retry_count: u32,
73    pub created_at: DateTime<Utc>,
74    pub started_at: Option<DateTime<Utc>>,
75    pub completed_at: Option<DateTime<Utc>>,
76    pub duration_ms: Option<u64>,
77    pub error_message: Option<String>,
78    pub worker_id: Option<usize>,
79}
80
81impl TaskState {
82    pub fn is_terminal(&self) -> bool {
83        matches!(self.status, TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled)
84    }
85
86    pub fn can_retry(&self) -> bool {
87        self.retry_count < MAX_RETRIES
88            && matches!(self.status, TaskStatus::Failed)
89            && self
90                .error_message
91                .as_ref()
92                .is_some_and(|msg| !msg.contains("permanent"))
93    }
94
95    pub fn age(&self) -> chrono::Duration {
96        Utc::now().signed_duration_since(self.created_at)
97    }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct JobMetrics {
102    pub status: TaskStatus,
103    pub created_at: DateTime<Utc>,
104    pub started_at: Option<DateTime<Utc>>,
105    pub retry_count: u32,
106    pub error_message: Option<String>,
107    pub duration_ms: Option<u64>,
108    pub worker_id: Option<usize>,
109}
110
111impl From<&TaskState> for JobMetrics {
112    fn from(state: &TaskState) -> Self {
113        JobMetrics {
114            status: state.status.clone(),
115            created_at: state.created_at,
116            started_at: state.started_at,
117            retry_count: state.retry_count,
118            error_message: state.error_message.clone(),
119            duration_ms: state.duration_ms,
120            worker_id: state.worker_id,
121        }
122    }
123}
124
125#[derive(Debug, Clone)]
126pub(crate) struct QueuedTask {
127    pub id: String,
128    pub task_name: String,
129    pub task_data: Vec<u8>,
130    pub retry_count: u32,
131    pub created_at: std::time::Instant,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct HealthStatus {
136    pub status: String,
137    pub queue_depth: u64,
138    pub max_queue_size: usize,
139    pub timestamp: DateTime<Utc>,
140    pub accepting_tasks: bool,
141}
142
143impl HealthStatus {
144    pub fn healthy(queue_depth: u64) -> Self {
145        Self {
146            status: "healthy".to_string(),
147            queue_depth,
148            max_queue_size: MAX_QUEUE_SIZE,
149            timestamp: Utc::now(),
150            accepting_tasks: queue_depth < (MAX_QUEUE_SIZE as u64 / 2),
151        }
152    }
153
154    pub fn degraded(queue_depth: u64) -> Self {
155        Self {
156            status: "degraded".to_string(),
157            queue_depth,
158            max_queue_size: MAX_QUEUE_SIZE,
159            timestamp: Utc::now(),
160            accepting_tasks: queue_depth < MAX_QUEUE_SIZE as u64,
161        }
162    }
163
164    pub fn unhealthy(queue_depth: u64) -> Self {
165        Self {
166            status: "unhealthy".to_string(),
167            queue_depth,
168            max_queue_size: MAX_QUEUE_SIZE,
169            timestamp: Utc::now(),
170            accepting_tasks: false,
171        }
172    }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct CachedJobResult {
177    pub job_id: String,
178    pub completed_at: DateTime<Utc>,
179    pub success: bool,
180    pub data: serde_json::Value, // The actual response/output data
181    pub error: Option<String>,   // Error message if failed
182    pub ttl: Option<Duration>,   // Optional TTL for auto-cleanup
183}