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
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct TaskTreeInput {
96 #[serde(rename = "ref")]
100 pub ref_id: Option<String>,
101
102 pub id: Option<String>,
105
106 #[serde(default)]
108 pub title: String,
109
110 pub description: Option<String>,
112
113 pub priority: Option<Priority>,
115
116 pub points: Option<i32>,
118
119 pub time_estimate_ms: Option<i64>,
121
122 pub needed_tags: Option<Vec<String>>,
124
125 pub wanted_tags: Option<Vec<String>>,
127
128 pub tags: Option<Vec<String>>,
130
131 #[serde(default)]
133 pub children: Vec<TaskTreeInput>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct Dependency {
140 pub from_task_id: String,
141 pub to_task_id: String,
142 pub dep_type: String,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct FileLock {
149 pub file_path: String,
150 pub worker_id: String,
151 pub reason: Option<String>,
152 pub locked_at: i64,
153 pub task_id: Option<String>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct ClaimEvent {
159 pub id: i64,
160 pub file_path: String,
161 pub worker_id: String,
162 pub event: ClaimEventType,
163 pub reason: Option<String>,
164 pub timestamp: i64,
165 pub end_timestamp: Option<i64>,
166 pub claim_id: Option<i64>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct TaskStateEvent {
173 pub id: i64,
174 pub task_id: String,
175 pub worker_id: Option<String>,
176 pub event: String,
177 pub reason: Option<String>,
178 pub timestamp: i64,
179 pub end_timestamp: Option<i64>,
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "snake_case")]
185pub enum ClaimEventType {
186 Claimed,
187 Released,
188}
189
190impl ClaimEventType {
191 pub fn as_str(&self) -> &'static str {
192 match self {
193 ClaimEventType::Claimed => "claimed",
194 ClaimEventType::Released => "released",
195 }
196 }
197
198 pub fn from_str(s: &str) -> Option<Self> {
199 match s {
200 "claimed" => Some(ClaimEventType::Claimed),
201 "released" => Some(ClaimEventType::Released),
202 _ => None,
203 }
204 }
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct ClaimUpdates {
210 pub new_claims: Vec<ClaimEvent>,
211 pub dropped_claims: Vec<ClaimEvent>,
212 pub sequence: i64,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct Attachment {
220 pub task_id: String,
221 pub order_index: i32,
222 pub name: String,
223 pub mime_type: String,
224 pub content: String,
225 #[serde(skip_serializing_if = "Option::is_none")]
228 pub file_path: Option<String>,
229 pub created_at: i64,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct AttachmentMeta {
236 pub task_id: String,
237 pub order_index: i32,
238 pub name: String,
239 pub mime_type: String,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub file_path: Option<String>,
243 pub created_at: i64,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct Stats {
249 pub total_tasks: i64,
250 pub tasks_by_status: HashMap<String, i64>,
252 pub total_points: i64,
253 pub completed_points: i64,
254 pub total_time_estimate_ms: i64,
255 pub total_time_actual_ms: i64,
256 pub total_cost_usd: f64,
257 pub total_metrics: [i64; 8],
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct TaskSummary {
264 pub id: String,
265 pub title: String,
266 pub status: String,
267 pub priority: Priority,
268 pub worker_id: Option<String>,
269 pub points: Option<i32>,
270 pub current_thought: Option<String>,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ScanResult {
277 pub root: Task,
279 pub before: Vec<Task>,
281 pub after: Vec<Task>,
283 pub above: Vec<Task>,
285 pub below: Vec<Task>,
287}
288
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct DisconnectSummary {
293 pub tasks_released: i32,
295 pub files_released: i32,
297 pub final_status: String,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct CleanupSummary {
304 pub workers_evicted: i32,
306 pub tasks_released: i32,
308 pub files_released: i32,
310 pub final_status: String,
312 pub evicted_worker_ids: Vec<String>,
314}
315
316#[cfg(test)]
317mod tests {
318 }