1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Worker {
9 pub id: String,
10 pub tags: Vec<String>,
11 pub max_claims: i32,
12 pub registered_at: i64,
13 pub last_heartbeat: i64,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct WorkerInfo {
19 pub id: String,
20 pub tags: Vec<String>,
21 pub max_claims: i32,
22 pub claim_count: i32,
23 pub current_thought: Option<String>,
24 pub registered_at: i64,
25 pub last_heartbeat: i64,
26}
27
28pub type Priority = i32;
31
32pub const PRIORITY_DEFAULT: Priority = 5;
34
35pub fn parse_priority(s: &str) -> Priority {
37 s.parse().unwrap_or(PRIORITY_DEFAULT).clamp(0, 10)
38}
39
40pub fn clamp_priority(p: Priority) -> Priority {
42 p.clamp(0, 10)
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct Task {
48 pub id: String,
49 pub title: String,
50 pub description: Option<String>,
51 pub status: String,
52 pub priority: Priority,
53 pub worker_id: Option<String>,
54 pub claimed_at: Option<i64>,
55
56 pub needed_tags: Vec<String>,
58 pub wanted_tags: Vec<String>,
59
60 pub tags: Vec<String>,
62
63 pub points: Option<i32>,
65 pub time_estimate_ms: Option<i64>,
66 pub time_actual_ms: Option<i64>,
67 pub started_at: Option<i64>,
68 pub completed_at: Option<i64>,
69
70 pub current_thought: Option<String>,
72
73 pub cost_usd: f64,
75 pub metrics: [i64; 8],
77
78 pub created_at: i64,
79 pub updated_at: i64,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct TaskTree {
85 #[serde(flatten)]
86 pub task: Task,
87 pub children: Vec<TaskTree>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct TaskTreeInput {
94 #[serde(rename = "ref")]
98 pub ref_id: Option<String>,
99
100 pub id: Option<String>,
103
104 #[serde(default)]
106 pub title: String,
107
108 pub description: Option<String>,
110
111 pub priority: Option<Priority>,
113
114 pub points: Option<i32>,
116
117 pub time_estimate_ms: Option<i64>,
119
120 pub needed_tags: Option<Vec<String>>,
122
123 pub wanted_tags: Option<Vec<String>>,
125
126 pub tags: Option<Vec<String>>,
128
129 #[serde(default)]
131 pub children: Vec<TaskTreeInput>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct Dependency {
138 pub from_task_id: String,
139 pub to_task_id: String,
140 pub dep_type: String,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct FileLock {
147 pub file_path: String,
148 pub worker_id: String,
149 pub reason: Option<String>,
150 pub locked_at: i64,
151 pub task_id: Option<String>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct ClaimEvent {
157 pub id: i64,
158 pub file_path: String,
159 pub worker_id: String,
160 pub event: ClaimEventType,
161 pub reason: Option<String>,
162 pub timestamp: i64,
163 pub end_timestamp: Option<i64>,
164 pub claim_id: Option<i64>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct TaskStateEvent {
171 pub id: i64,
172 pub task_id: String,
173 pub worker_id: Option<String>,
174 pub event: String,
175 pub reason: Option<String>,
176 pub timestamp: i64,
177 pub end_timestamp: Option<i64>,
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
182#[serde(rename_all = "snake_case")]
183pub enum ClaimEventType {
184 Claimed,
185 Released,
186}
187
188impl ClaimEventType {
189 pub fn as_str(&self) -> &'static str {
190 match self {
191 ClaimEventType::Claimed => "claimed",
192 ClaimEventType::Released => "released",
193 }
194 }
195
196 pub fn parse(s: &str) -> Option<Self> {
197 match s {
198 "claimed" => Some(ClaimEventType::Claimed),
199 "released" => Some(ClaimEventType::Released),
200 _ => None,
201 }
202 }
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct ClaimUpdates {
208 pub new_claims: Vec<ClaimEvent>,
209 pub dropped_claims: Vec<ClaimEvent>,
210 pub sequence: i64,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct Attachment {
218 pub task_id: String,
219 pub order_index: i32,
220 pub name: String,
221 pub mime_type: String,
222 pub content: String,
223 #[serde(skip_serializing_if = "Option::is_none")]
226 pub file_path: Option<String>,
227 pub created_at: i64,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct AttachmentMeta {
234 pub task_id: String,
235 pub order_index: i32,
236 pub name: String,
237 pub mime_type: String,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub file_path: Option<String>,
241 pub created_at: i64,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct Stats {
247 pub total_tasks: i64,
248 pub tasks_by_status: HashMap<String, i64>,
250 pub total_points: i64,
251 pub completed_points: i64,
252 pub total_time_estimate_ms: i64,
253 pub total_time_actual_ms: i64,
254 pub total_cost_usd: f64,
255 pub total_metrics: [i64; 8],
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct TaskSummary {
262 pub id: String,
263 pub title: String,
264 pub status: String,
265 pub priority: Priority,
266 pub worker_id: Option<String>,
267 pub points: Option<i32>,
268 pub current_thought: Option<String>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct ScanResult {
275 pub root: Task,
277 pub before: Vec<Task>,
279 pub after: Vec<Task>,
281 pub above: Vec<Task>,
283 pub below: Vec<Task>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct DisconnectSummary {
290 pub tasks_released: i32,
292 pub files_released: i32,
294 pub final_status: String,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct CleanupSummary {
301 pub workers_evicted: i32,
303 pub tasks_released: i32,
305 pub files_released: i32,
307 pub final_status: String,
309 pub evicted_worker_ids: Vec<String>,
311}
312
313#[cfg(test)]
314mod tests {
315 }