Skip to main content

snerd_rust/
task.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Serialize, Deserialize, Clone)]
5pub struct JobErrorReturn {
6    #[serde(rename = "error")]
7    pub error_string: String,
8    pub retry_worthy: bool,
9}
10
11#[derive(Debug, Serialize, Deserialize, Clone)]
12pub struct ProgressMessage {
13    #[serde(rename = "task_id")]
14    pub task_id: String,
15    
16    #[serde(rename = "data")]
17    pub data: String,
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
21pub struct RetryableTask {
22    #[serde(rename = "taskId")]
23    pub task_id: String,
24
25    #[serde(rename = "retryCount")]
26    pub retry_count: i32,
27
28    #[serde(rename = "maxRetries")]
29    pub max_retries: i32,
30
31    #[serde(rename = "retryAfterHours")]
32    pub retry_after_hours: f64,
33
34    #[serde(rename = "retryAfterTime")]
35    pub retry_after_time: DateTime<Utc>,
36
37    #[serde(rename = "taskData")]
38    pub task_data: String,
39
40    #[serde(rename = "taskType")]
41    pub task_type: String,
42
43    #[serde(rename = "LastErrorObj", skip_serializing_if = "Option::is_none")]
44    pub last_error_obj: Option<String>,
45
46    #[serde(rename = "LastJobError", skip_serializing_if = "Option::is_none")]
47    pub last_job_error: Option<JobErrorReturn>,
48
49    #[serde(rename = "rateLimitGroup", skip_serializing_if = "Option::is_none")]
50    pub rate_limit_group: Option<String>,
51
52    #[serde(rename = "maxPerMinute", skip_serializing_if = "Option::is_none")]
53    pub max_per_minute: Option<i32>,
54
55    #[serde(rename = "autoDedupe", skip_serializing_if = "Option::is_none")]
56    pub auto_dedupe: Option<bool>,
57
58    #[serde(rename = "urgencyScore", skip_serializing_if = "Option::is_none")]
59    pub urgency_score: Option<f64>,
60
61    #[serde(rename = "payloadHash", skip_serializing_if = "Option::is_none")]
62    pub payload_hash: Option<String>,
63
64    #[serde(rename = "deletedAt", skip_serializing_if = "Option::is_none")]
65    pub deleted_at: Option<DateTime<Utc>>,
66
67    #[serde(skip, default = "Utc::now")]
68    pub created_at: DateTime<Utc>,
69
70    #[serde(skip, default = "Utc::now")]
71    pub updated_at: DateTime<Utc>,
72}
73
74impl RetryableTask {
75    pub fn new(
76        task_id: String,
77        task_type: String,
78        task_data: String,
79        max_retries: i32,
80        retry_after_hours: f64,
81        rate_limit_group: Option<String>,
82        max_per_minute: Option<i32>,
83        auto_dedupe: Option<bool>,
84        urgency_score: Option<f64>,
85    ) -> Self {
86        let now = Utc::now();
87        let payload_hash = if auto_dedupe.unwrap_or(false) {
88            use xxhash_rust::xxh64::xxh64;
89            let combined = format!("{}{}", task_type, task_data);
90            Some(format!("{:x}", xxh64(combined.as_bytes(), 0)))
91        } else {
92            None
93        };
94        
95        Self {
96            task_id,
97            task_type,
98            task_data,
99            max_retries,
100            retry_after_hours,
101            retry_count: 0,
102            retry_after_time: now,
103            last_error_obj: None,
104            last_job_error: None,
105            rate_limit_group,
106            max_per_minute,
107            auto_dedupe,
108            urgency_score,
109            payload_hash,
110            deleted_at: None,
111            created_at: now,
112            updated_at: now,
113        }
114    }
115
116    pub fn mark_deleted(&mut self) {
117        self.deleted_at = Some(Utc::now());
118        self.updated_at = Utc::now();
119    }
120
121    pub fn update_retry_config(&mut self, error_msg: Option<String>) {
122        self.retry_count += 1;
123
124        // Calculate next retry time
125        let seconds = (self.retry_after_hours * 3600.0) as i64;
126        self.retry_after_time = Utc::now() + chrono::Duration::seconds(seconds);
127
128        self.last_error_obj = error_msg.clone();
129
130        if let Some(msg) = error_msg {
131            self.last_job_error = Some(JobErrorReturn {
132                error_string: msg,
133                retry_worthy: true,
134            });
135        } else {
136            self.last_job_error = None;
137        }
138
139        self.updated_at = Utc::now();
140    }
141}
142
143use std::cmp::Ordering;
144
145#[derive(Clone)]
146pub struct PriorityTask(pub RetryableTask);
147
148impl PartialEq for PriorityTask {
149    fn eq(&self, other: &Self) -> bool {
150        self.0.task_id == other.0.task_id
151    }
152}
153
154impl Eq for PriorityTask {}
155
156impl PartialOrd for PriorityTask {
157    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
158        Some(self.cmp(other))
159    }
160}
161
162impl Ord for PriorityTask {
163    fn cmp(&self, other: &Self) -> Ordering {
164        let score_a = self.0.urgency_score.unwrap_or(0.0);
165        let score_b = other.0.urgency_score.unwrap_or(0.0);
166        
167        // Reverse order so the max score is popped first
168        score_a.partial_cmp(&score_b).unwrap_or(Ordering::Equal)
169    }
170}