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}
51
52impl fmt::Display for TaskStatus {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        match self {
55            TaskStatus::Queued => write!(f, "queued"),
56            TaskStatus::InProgress => write!(f, "in_progress"),
57            TaskStatus::Completed => write!(f, "completed"),
58            TaskStatus::Failed => write!(f, "failed"),
59            TaskStatus::Retrying => write!(f, "retrying"),
60        }
61    }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct TaskState {
66    pub id: String,
67    pub task_name: String,
68    pub task_data: serde_json::Value,
69    pub status: TaskStatus,
70    pub retry_count: u32,
71    pub created_at: DateTime<Utc>,
72    pub started_at: Option<DateTime<Utc>>,
73    pub completed_at: Option<DateTime<Utc>>,
74    pub duration_ms: Option<u64>,
75    pub error_message: Option<String>,
76    pub worker_id: Option<usize>,
77}
78
79impl TaskState {
80    pub fn is_terminal(&self) -> bool {
81        matches!(self.status, TaskStatus::Completed | TaskStatus::Failed)
82    }
83
84    pub fn can_retry(&self) -> bool {
85        self.retry_count < MAX_RETRIES
86            && matches!(self.status, TaskStatus::Failed)
87            && self
88                .error_message
89                .as_ref()
90                .is_some_and(|msg| !msg.contains("permanent"))
91    }
92
93    pub fn age(&self) -> chrono::Duration {
94        Utc::now().signed_duration_since(self.created_at)
95    }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct JobMetrics {
100    pub status: TaskStatus,
101    pub created_at: DateTime<Utc>,
102    pub started_at: Option<DateTime<Utc>>,
103    pub retry_count: u32,
104    pub error_message: Option<String>,
105    pub duration_ms: Option<u64>,
106    pub worker_id: Option<usize>,
107}
108
109impl From<&TaskState> for JobMetrics {
110    fn from(state: &TaskState) -> Self {
111        JobMetrics {
112            status: state.status.clone(),
113            created_at: state.created_at,
114            started_at: state.started_at,
115            retry_count: state.retry_count,
116            error_message: state.error_message.clone(),
117            duration_ms: state.duration_ms,
118            worker_id: state.worker_id,
119        }
120    }
121}
122
123#[derive(Debug, Clone)]
124pub(crate) struct QueuedTask {
125    pub id: String,
126    pub task_name: String,
127    pub task_data: Vec<u8>,
128    pub retry_count: u32,
129    pub created_at: std::time::Instant,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct HealthStatus {
134    pub status: String,
135    pub queue_depth: u64,
136    pub max_queue_size: usize,
137    pub timestamp: DateTime<Utc>,
138    pub accepting_tasks: bool,
139}
140
141impl HealthStatus {
142    pub fn healthy(queue_depth: u64) -> Self {
143        Self {
144            status: "healthy".to_string(),
145            queue_depth,
146            max_queue_size: MAX_QUEUE_SIZE,
147            timestamp: Utc::now(),
148            accepting_tasks: queue_depth < (MAX_QUEUE_SIZE as u64 / 2),
149        }
150    }
151
152    pub fn degraded(queue_depth: u64) -> Self {
153        Self {
154            status: "degraded".to_string(),
155            queue_depth,
156            max_queue_size: MAX_QUEUE_SIZE,
157            timestamp: Utc::now(),
158            accepting_tasks: queue_depth < MAX_QUEUE_SIZE as u64,
159        }
160    }
161
162    pub fn unhealthy(queue_depth: u64) -> Self {
163        Self {
164            status: "unhealthy".to_string(),
165            queue_depth,
166            max_queue_size: MAX_QUEUE_SIZE,
167            timestamp: Utc::now(),
168            accepting_tasks: false,
169        }
170    }
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct CachedJobResult {
175    pub job_id: String,
176    pub completed_at: DateTime<Utc>,
177    pub success: bool,
178    pub data: serde_json::Value, // The actual response/output data
179    pub error: Option<String>,   // Error message if failed
180    pub ttl: Option<Duration>,   // Optional TTL for auto-cleanup
181}