vdsl_sync/application/task.rs
1//! Background task types for long-running sync operations.
2//!
3//! Provides shared types (`TaskId`, `TaskStatus`) used by interface layers
4//! (MCP, Lua) to manage background sync tasks. The actual task registry
5//! lives in the interface layer — Store itself is synchronous.
6//!
7//! # Design
8//!
9//! - `TaskId`: opaque UUID string identifying a spawned task
10//! - `TaskStatus`: Pending | Running(phase) | Completed(T) | Failed(String)
11//!
12//! # Progress reporting
13//!
14//! `Running(String)` carries a human-readable phase description so that
15//! poll() callers can display what the task is currently doing.
16//! Example phases: "scanning 5000 files", "recovering 12 failed transfers",
17//! "transferring 45/200 queued".
18
19use uuid::Uuid;
20
21/// Opaque task identifier (UUID v4).
22#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
23pub struct TaskId(String);
24
25impl TaskId {
26 pub fn new() -> Self {
27 Self(Uuid::new_v4().to_string())
28 }
29
30 pub fn as_str(&self) -> &str {
31 &self.0
32 }
33
34 /// Reconstruct a TaskId from a string (e.g., from Lua poll call).
35 pub fn parse(s: &str) -> Self {
36 Self(s.to_string())
37 }
38}
39
40impl Default for TaskId {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl std::fmt::Display for TaskId {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.write_str(&self.0)
49 }
50}
51
52/// Status of a background task.
53///
54/// # Serde output
55///
56/// ```json
57/// {"status":"pending"}
58/// {"status":"running","result":"scanning 5000 files..."}
59/// {"status":"completed","result":{"scanned":5000,...}}
60/// {"status":"failed","result":"rclone: exit code 1"}
61/// ```
62#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
63#[serde(tag = "status", content = "result")]
64pub enum TaskStatus<T: Clone> {
65 /// Task is spawned but not yet started processing.
66 #[serde(rename = "pending")]
67 Pending,
68 /// Task is actively running. The String describes the current phase.
69 ///
70 /// Example phases:
71 /// - `"scanning 5000 files..."`
72 /// - `"recovering 12 failed transfers..."`
73 /// - `"transferring 45/200 queued..."`
74 #[serde(rename = "running")]
75 Running(String),
76 /// Task completed successfully with a result.
77 #[serde(rename = "completed")]
78 Completed(T),
79 /// Task failed with an error message.
80 #[serde(rename = "failed")]
81 Failed(String),
82}
83
84impl<T: Clone> TaskStatus<T> {
85 pub fn is_terminal(&self) -> bool {
86 matches!(self, Self::Completed(_) | Self::Failed(_))
87 }
88}