1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum TaskStatus {
12 Queued,
14 Executing,
16 Completed,
18 Failed,
20 Cancelled,
22}
23
24impl std::fmt::Display for TaskStatus {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::Queued => write!(f, "Queued"),
28 Self::Executing => write!(f, "Executing"),
29 Self::Completed => write!(f, "Completed"),
30 Self::Failed => write!(f, "Failed"),
31 Self::Cancelled => write!(f, "Cancelled"),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct TaskRequest {
39 pub id: String,
41 pub sectors: Vec<String>,
43 pub operation: String,
45 pub input: String,
47 pub priority: u32,
49 pub deadline: String,
51}
52
53impl TaskRequest {
54 #[must_use]
56 pub fn new(id: String, sector: String, operation: String, input: String) -> Self {
57 Self {
58 id,
59 sectors: vec![sector],
60 operation,
61 input,
62 priority: 0,
63 deadline: "2099-12-31T23:59:59Z".to_string(),
64 }
65 }
66
67 #[must_use]
69 pub const fn with_priority(mut self, priority: u32) -> Self {
70 self.priority = priority;
71 self
72 }
73
74 #[must_use]
76 pub fn add_sector(mut self, sector: String) -> Self {
77 if !self.sectors.contains(§or) {
78 self.sectors.push(sector);
79 }
80 self
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct TaskReceipt {
87 pub task_id: String,
89 pub agent_id: String,
91 pub sectors: Vec<String>,
93 pub status: TaskStatus,
95 pub result: String,
97 pub execution_time_ms: u64,
99 pub timestamp: String,
101 pub result_merkle: String,
103 pub metadata: HashMap<String, String>,
105}
106
107impl TaskReceipt {
108 #[must_use]
110 pub fn new(
111 task_id: String,
112 agent_id: String,
113 sectors: Vec<String>,
114 status: TaskStatus,
115 result: String,
116 ) -> Self {
117 Self {
118 task_id,
119 agent_id,
120 sectors,
121 status,
122 result,
123 execution_time_ms: 0,
124 timestamp: chrono::Utc::now().to_rfc3339(),
125 result_merkle: String::new(),
126 metadata: HashMap::new(),
127 }
128 }
129
130 #[must_use]
132 pub const fn with_execution_time(mut self, ms: u64) -> Self {
133 self.execution_time_ms = ms;
134 self
135 }
136
137 #[must_use]
139 pub fn with_merkle(mut self, merkle: String) -> Self {
140 self.result_merkle = merkle;
141 self
142 }
143
144 #[must_use]
146 pub fn add_metadata(mut self, key: String, value: String) -> Self {
147 self.metadata.insert(key, value);
148 self
149 }
150
151 #[must_use]
153 pub fn is_success(&self) -> bool {
154 self.status == TaskStatus::Completed
155 }
156}
157
158#[derive(Debug, Clone)]
160pub struct TaskQueue {
161 tasks: Vec<TaskRequest>,
163 receipts: Vec<TaskReceipt>,
165}
166
167impl TaskQueue {
168 #[must_use]
170 pub const fn new() -> Self {
171 Self { tasks: Vec::new(), receipts: Vec::new() }
172 }
173
174 pub fn enqueue(&mut self, task: TaskRequest) {
176 self.tasks.push(task);
177 self.tasks.sort_by(|a, b| b.priority.cmp(&a.priority));
179 }
180
181 pub fn dequeue(&mut self) -> Option<TaskRequest> {
183 if self.tasks.is_empty() {
184 None
185 } else {
186 Some(self.tasks.remove(0))
187 }
188 }
189
190 pub fn record_receipt(&mut self, receipt: TaskReceipt) {
192 self.receipts.push(receipt);
193 }
194
195 #[must_use]
197 pub const fn task_count(&self) -> usize {
198 self.tasks.len()
199 }
200
201 #[must_use]
203 pub const fn receipt_count(&self) -> usize {
204 self.receipts.len()
205 }
206
207 #[must_use]
209 pub fn receipts(&self) -> &[TaskReceipt] {
210 &self.receipts
211 }
212}
213
214impl Default for TaskQueue {
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn test_task_request_creation() {
226 let task = TaskRequest::new(
227 "task-001".to_string(),
228 "Academic".to_string(),
229 "desk-review".to_string(),
230 "paper data".to_string(),
231 );
232
233 assert_eq!(task.id, "task-001");
234 assert_eq!(task.sectors, vec!["Academic"]);
235 assert_eq!(task.priority, 0);
236 }
237
238 #[test]
239 fn test_task_with_priority() {
240 let task = TaskRequest::new(
241 "task-001".to_string(),
242 "Academic".to_string(),
243 "desk-review".to_string(),
244 "paper data".to_string(),
245 )
246 .with_priority(100);
247
248 assert_eq!(task.priority, 100);
249 }
250
251 #[test]
252 fn test_task_multi_sector() {
253 let task = TaskRequest::new(
254 "task-001".to_string(),
255 "Academic".to_string(),
256 "review".to_string(),
257 "data".to_string(),
258 )
259 .add_sector("Claims".to_string());
260
261 assert_eq!(task.sectors.len(), 2);
262 assert!(task.sectors.contains(&"Academic".to_string()));
263 assert!(task.sectors.contains(&"Claims".to_string()));
264 }
265
266 #[test]
267 fn test_task_receipt_creation() {
268 let receipt = TaskReceipt::new(
269 "task-001".to_string(),
270 "agent-1".to_string(),
271 vec!["Academic".to_string()],
272 TaskStatus::Completed,
273 "success".to_string(),
274 );
275
276 assert_eq!(receipt.task_id, "task-001");
277 assert_eq!(receipt.agent_id, "agent-1");
278 assert!(receipt.is_success());
279 }
280
281 #[test]
282 fn test_task_queue() {
283 let mut queue = TaskQueue::new();
284
285 queue.enqueue(TaskRequest::new(
286 "t1".to_string(),
287 "Academic".to_string(),
288 "op".to_string(),
289 "data".to_string(),
290 ));
291
292 queue.enqueue(
293 TaskRequest::new(
294 "t2".to_string(),
295 "Claims".to_string(),
296 "op".to_string(),
297 "data".to_string(),
298 )
299 .with_priority(10),
300 );
301
302 let task = queue.dequeue().unwrap();
304 assert_eq!(task.id, "t2");
305 }
306
307 #[test]
308 fn test_task_status_display() {
309 assert_eq!(TaskStatus::Queued.to_string(), "Queued");
310 assert_eq!(TaskStatus::Completed.to_string(), "Completed");
311 assert_eq!(TaskStatus::Failed.to_string(), "Failed");
312 }
313}