1#![allow(dead_code)]
8
9use std::cmp::Ordering;
10use std::collections::BinaryHeap;
11use uuid::Uuid;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
15pub enum TaskPriority {
16 Background = 0,
18 Normal = 1,
20 High = 2,
22 Urgent = 3,
24 Critical = 4,
26}
27
28impl TaskPriority {
29 #[must_use]
31 pub fn value(&self) -> u8 {
32 *self as u8
33 }
34
35 #[must_use]
37 pub fn is_higher_than(&self, other: &Self) -> bool {
38 self.value() > other.value()
39 }
40}
41
42impl PartialOrd for TaskPriority {
43 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
44 Some(self.cmp(other))
45 }
46}
47
48impl Ord for TaskPriority {
49 fn cmp(&self, other: &Self) -> Ordering {
50 self.value().cmp(&other.value())
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
56pub enum TaskStatus {
57 Pending,
59 Assigned,
61 Running,
63 Completed,
65 Failed,
67 Cancelled,
69}
70
71#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
73pub struct DistributedTask {
74 pub id: Uuid,
76 pub name: String,
78 pub priority: TaskPriority,
80 pub status: TaskStatus,
82 pub payload: String,
84 pub enqueued_at: i64,
86 pub max_retries: u32,
88 pub retry_count: u32,
90 pub deadline: Option<i64>,
92 sequence: u64,
94}
95
96impl DistributedTask {
97 #[must_use]
99 pub fn new(name: &str, priority: TaskPriority, payload: &str) -> Self {
100 Self {
101 id: Uuid::new_v4(),
102 name: name.to_string(),
103 priority,
104 status: TaskStatus::Pending,
105 payload: payload.to_string(),
106 enqueued_at: chrono::Utc::now().timestamp(),
107 max_retries: 3,
108 retry_count: 0,
109 deadline: None,
110 sequence: 0,
111 }
112 }
113
114 #[must_use]
116 pub fn with_max_retries(mut self, retries: u32) -> Self {
117 self.max_retries = retries;
118 self
119 }
120
121 #[must_use]
123 pub fn with_deadline(mut self, deadline: i64) -> Self {
124 self.deadline = Some(deadline);
125 self
126 }
127
128 #[must_use]
130 pub fn can_retry(&self) -> bool {
131 self.retry_count < self.max_retries
132 }
133
134 #[must_use]
136 pub fn is_past_deadline(&self, now: i64) -> bool {
137 self.deadline.is_some_and(|d| now > d)
138 }
139
140 pub fn retry(&mut self) {
142 self.retry_count += 1;
143 self.status = TaskStatus::Pending;
144 }
145
146 pub fn mark_running(&mut self) {
148 self.status = TaskStatus::Running;
149 }
150
151 pub fn mark_completed(&mut self) {
153 self.status = TaskStatus::Completed;
154 }
155
156 pub fn mark_failed(&mut self) {
158 self.status = TaskStatus::Failed;
159 }
160}
161
162impl PartialEq for DistributedTask {
163 fn eq(&self, other: &Self) -> bool {
164 self.id == other.id
165 }
166}
167
168impl Eq for DistributedTask {}
169
170impl PartialOrd for DistributedTask {
171 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
172 Some(self.cmp(other))
173 }
174}
175
176impl Ord for DistributedTask {
177 fn cmp(&self, other: &Self) -> Ordering {
178 match self.priority.cmp(&other.priority) {
180 Ordering::Equal => other.sequence.cmp(&self.sequence), other_ord => other_ord,
182 }
183 }
184}
185
186#[derive(Debug)]
191pub struct TaskQueue {
192 heap: BinaryHeap<DistributedTask>,
194 next_sequence: u64,
196 max_capacity: usize,
198 total_enqueued: u64,
200 total_dequeued: u64,
202}
203
204impl TaskQueue {
205 #[must_use]
207 pub fn new() -> Self {
208 Self {
209 heap: BinaryHeap::new(),
210 next_sequence: 0,
211 max_capacity: 0,
212 total_enqueued: 0,
213 total_dequeued: 0,
214 }
215 }
216
217 #[must_use]
219 pub fn with_capacity(max_capacity: usize) -> Self {
220 Self {
221 heap: BinaryHeap::new(),
222 next_sequence: 0,
223 max_capacity,
224 total_enqueued: 0,
225 total_dequeued: 0,
226 }
227 }
228
229 pub fn enqueue(&mut self, mut task: DistributedTask) -> bool {
231 if self.max_capacity > 0 && self.heap.len() >= self.max_capacity {
232 return false;
233 }
234 task.sequence = self.next_sequence;
235 self.next_sequence += 1;
236 self.total_enqueued += 1;
237 self.heap.push(task);
238 true
239 }
240
241 pub fn dequeue(&mut self) -> Option<DistributedTask> {
245 let task = self.heap.pop()?;
246 self.total_dequeued += 1;
247 Some(task)
248 }
249
250 #[must_use]
252 pub fn peek(&self) -> Option<&DistributedTask> {
253 self.heap.peek()
254 }
255
256 #[must_use]
258 pub fn len(&self) -> usize {
259 self.heap.len()
260 }
261
262 #[must_use]
264 pub fn is_empty(&self) -> bool {
265 self.heap.is_empty()
266 }
267
268 #[must_use]
270 pub fn total_enqueued(&self) -> u64 {
271 self.total_enqueued
272 }
273
274 #[must_use]
276 pub fn total_dequeued(&self) -> u64 {
277 self.total_dequeued
278 }
279
280 pub fn drain(&mut self) -> Vec<DistributedTask> {
282 let mut result = Vec::with_capacity(self.heap.len());
283 while let Some(task) = self.heap.pop() {
284 result.push(task);
285 }
286 self.total_dequeued += result.len() as u64;
287 result
288 }
289
290 pub fn remove_expired(&mut self, now: i64) -> Vec<DistributedTask> {
292 let mut remaining = Vec::new();
293 let mut expired = Vec::new();
294 while let Some(task) = self.heap.pop() {
295 if task.is_past_deadline(now) {
296 expired.push(task);
297 } else {
298 remaining.push(task);
299 }
300 }
301 for task in remaining {
302 self.heap.push(task);
303 }
304 expired
305 }
306}
307
308impl Default for TaskQueue {
309 fn default() -> Self {
310 Self::new()
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317
318 #[test]
319 fn test_task_priority_ordering() {
320 assert!(TaskPriority::Critical > TaskPriority::Urgent);
321 assert!(TaskPriority::Urgent > TaskPriority::High);
322 assert!(TaskPriority::High > TaskPriority::Normal);
323 assert!(TaskPriority::Normal > TaskPriority::Background);
324 }
325
326 #[test]
327 fn test_task_priority_is_higher_than() {
328 assert!(TaskPriority::Critical.is_higher_than(&TaskPriority::High));
329 assert!(!TaskPriority::Normal.is_higher_than(&TaskPriority::High));
330 }
331
332 #[test]
333 fn test_task_creation() {
334 let task =
335 DistributedTask::new("encode_video", TaskPriority::Normal, "{\"file\":\"a.mp4\"}");
336 assert_eq!(task.name, "encode_video");
337 assert_eq!(task.priority, TaskPriority::Normal);
338 assert_eq!(task.status, TaskStatus::Pending);
339 assert_eq!(task.retry_count, 0);
340 }
341
342 #[test]
343 fn test_task_with_deadline() {
344 let task = DistributedTask::new("t1", TaskPriority::High, "{}").with_deadline(9999);
345 assert_eq!(task.deadline, Some(9999));
346 assert!(!task.is_past_deadline(9998));
347 assert!(task.is_past_deadline(10000));
348 }
349
350 #[test]
351 fn test_task_retry() {
352 let mut task = DistributedTask::new("t1", TaskPriority::Normal, "{}").with_max_retries(2);
353 assert!(task.can_retry());
354 task.retry();
355 assert_eq!(task.retry_count, 1);
356 task.retry();
357 assert!(!task.can_retry());
358 }
359
360 #[test]
361 fn test_task_lifecycle() {
362 let mut task = DistributedTask::new("t1", TaskPriority::Normal, "{}");
363 assert_eq!(task.status, TaskStatus::Pending);
364 task.mark_running();
365 assert_eq!(task.status, TaskStatus::Running);
366 task.mark_completed();
367 assert_eq!(task.status, TaskStatus::Completed);
368 }
369
370 #[test]
371 fn test_queue_enqueue_dequeue() {
372 let mut q = TaskQueue::new();
373 q.enqueue(DistributedTask::new("t1", TaskPriority::Normal, "{}"));
374 assert_eq!(q.len(), 1);
375 let t = q.dequeue().expect("dequeue should return a task");
376 assert_eq!(t.name, "t1");
377 assert!(q.is_empty());
378 }
379
380 #[test]
381 fn test_queue_priority_order() {
382 let mut q = TaskQueue::new();
383 q.enqueue(DistributedTask::new("low", TaskPriority::Background, "{}"));
384 q.enqueue(DistributedTask::new("high", TaskPriority::High, "{}"));
385 q.enqueue(DistributedTask::new("normal", TaskPriority::Normal, "{}"));
386 let first = q.dequeue().expect("dequeue should return a task");
387 assert_eq!(first.name, "high");
388 let second = q.dequeue().expect("dequeue should return a task");
389 assert_eq!(second.name, "normal");
390 let third = q.dequeue().expect("dequeue should return a task");
391 assert_eq!(third.name, "low");
392 }
393
394 #[test]
395 fn test_queue_fifo_within_same_priority() {
396 let mut q = TaskQueue::new();
397 q.enqueue(DistributedTask::new("first", TaskPriority::Normal, "{}"));
398 q.enqueue(DistributedTask::new("second", TaskPriority::Normal, "{}"));
399 q.enqueue(DistributedTask::new("third", TaskPriority::Normal, "{}"));
400 assert_eq!(
401 q.dequeue().expect("dequeue should return a task").name,
402 "first"
403 );
404 assert_eq!(
405 q.dequeue().expect("dequeue should return a task").name,
406 "second"
407 );
408 assert_eq!(
409 q.dequeue().expect("dequeue should return a task").name,
410 "third"
411 );
412 }
413
414 #[test]
415 fn test_queue_capacity_limit() {
416 let mut q = TaskQueue::with_capacity(2);
417 assert!(q.enqueue(DistributedTask::new("t1", TaskPriority::Normal, "{}")));
418 assert!(q.enqueue(DistributedTask::new("t2", TaskPriority::Normal, "{}")));
419 assert!(!q.enqueue(DistributedTask::new("t3", TaskPriority::Normal, "{}")));
420 assert_eq!(q.len(), 2);
421 }
422
423 #[test]
424 fn test_queue_peek() {
425 let mut q = TaskQueue::new();
426 assert!(q.peek().is_none());
427 q.enqueue(DistributedTask::new("t1", TaskPriority::High, "{}"));
428 assert_eq!(q.peek().expect("peek should return a value").name, "t1");
429 assert_eq!(q.len(), 1); }
431
432 #[test]
433 fn test_queue_drain() {
434 let mut q = TaskQueue::new();
435 q.enqueue(DistributedTask::new("t1", TaskPriority::Normal, "{}"));
436 q.enqueue(DistributedTask::new("t2", TaskPriority::High, "{}"));
437 let drained = q.drain();
438 assert_eq!(drained.len(), 2);
439 assert!(q.is_empty());
440 assert_eq!(drained[0].name, "t2"); }
442
443 #[test]
444 fn test_queue_remove_expired() {
445 let mut q = TaskQueue::new();
446 q.enqueue(DistributedTask::new("expired", TaskPriority::Normal, "{}").with_deadline(100));
447 q.enqueue(DistributedTask::new("alive", TaskPriority::Normal, "{}").with_deadline(9999));
448 q.enqueue(DistributedTask::new(
449 "no_deadline",
450 TaskPriority::Normal,
451 "{}",
452 ));
453 let expired = q.remove_expired(200);
454 assert_eq!(expired.len(), 1);
455 assert_eq!(expired[0].name, "expired");
456 assert_eq!(q.len(), 2);
457 }
458
459 #[test]
460 fn test_queue_counters() {
461 let mut q = TaskQueue::new();
462 q.enqueue(DistributedTask::new("t1", TaskPriority::Normal, "{}"));
463 q.enqueue(DistributedTask::new("t2", TaskPriority::Normal, "{}"));
464 let _ = q.dequeue();
465 assert_eq!(q.total_enqueued(), 2);
466 assert_eq!(q.total_dequeued(), 1);
467 }
468
469 #[test]
470 fn test_task_mark_failed() {
471 let mut task = DistributedTask::new("t1", TaskPriority::Normal, "{}");
472 task.mark_failed();
473 assert_eq!(task.status, TaskStatus::Failed);
474 }
475}