Skip to main content

tur_rs/engine/
types.rs

1use super::*;
2use crate::engine::scaler::ProtocolFamily;
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub enum DownloadStatus {
6    Queued,
7    Downloading,
8    Paused,
9    Stopped,
10    Completed,
11    Error(String),
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct DownloadTask {
16    pub id: Uuid,
17    pub url: String,
18    pub filename: String,
19    pub dir: PathBuf,
20    pub total_size: u64,
21    pub downloaded_size: u64,
22    pub connections: usize,
23    pub status: DownloadStatus,
24    pub speed: f64,
25    pub dry_run: bool,
26    pub dry_run_size_mb: Option<u64>,
27    pub borrow_limit_mb: u64,
28    pub min_connections: usize,
29    pub max_connections: usize,
30    pub per_download_bandwidth_limit_bps: u64,
31    pub schedule_mode: ScheduleMode,
32    pub http_mode: HttpMode,
33    pub log_root: Option<PathBuf>,
34    /// Session context for authenticated/session-aware downloading.
35    #[serde(skip)]
36    pub request_context: Option<crate::service::RequestContext>,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
40pub enum ScheduleMode {
41    Fib,
42    FibAdaptive,
43    Equal,
44}
45
46impl ScheduleMode {
47    pub fn parse(input: &str) -> Result<Self> {
48        match input.trim().to_ascii_lowercase().as_str() {
49            "fib" => Ok(Self::Fib),
50            "fib-adaptive" | "fib_adaptive" | "adaptive-fib" | "adaptive_fib" => {
51                Ok(Self::FibAdaptive)
52            }
53            "equal" => Ok(Self::Equal),
54            other => Err(anyhow!("unsupported schedule mode: {}", other)),
55        }
56    }
57
58    pub(crate) fn as_str(self) -> &'static str {
59        match self {
60            Self::Fib => "fib",
61            Self::FibAdaptive => "fib-adaptive",
62            Self::Equal => "equal",
63        }
64    }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
68pub enum HttpMode {
69    Auto,
70    Http1,
71    Http2,
72    Http3,
73}
74
75impl HttpMode {
76    pub fn parse(input: &str) -> Result<Self> {
77        match input.trim().to_ascii_lowercase().as_str() {
78            "auto" => Ok(Self::Auto),
79            "http1" | "http/1.1" | "h1" => Ok(Self::Http1),
80            "http2" | "http/2" | "h2" => Ok(Self::Http2),
81            "http3" | "http/3" | "h3" | "quic" => Ok(Self::Http3),
82            other => Err(anyhow!("unsupported http mode: {}", other)),
83        }
84    }
85
86    pub(crate) fn as_str(self) -> &'static str {
87        match self {
88            Self::Auto => "auto",
89            Self::Http1 => "http1",
90            Self::Http2 => "http2",
91            Self::Http3 => "http3",
92        }
93    }
94}
95
96#[derive(Debug)]
97pub struct ActiveRange {
98    pub id: u64,
99    pub label_start_mb: u64,
100    pub label_end_mb: u64,
101    pub byte_start: u64,
102    pub assigned_to: Cell<u32>,
103    pub cursor: Cell<u64>,
104    pub end: Cell<u64>,
105    pub parent_range_id: Option<u64>,
106    pub status: Cell<u8>,
107    pub last_sample_cursor: Cell<u64>,
108    pub last_sample_at_ms: Cell<u64>,
109    pub recent_speed_bps: Cell<u64>,
110}
111
112#[derive(Debug)]
113pub struct WorkRequest {
114    pub connection_id: u32,
115    pub tx: oneshot::Sender<Option<Rc<ActiveRange>>>,
116}
117
118#[derive(Debug, Clone)]
119pub enum EngineEvent {
120    Progress(Uuid, u64, f64),
121    StatusChanged(Uuid, DownloadStatus),
122    TotalSize(Uuid, u64),
123    Workers(Uuid, Vec<WorkerSnapshot>),
124    /// Protocol information — carries both requested HTTP mode and the
125    /// dominant negotiated protocol family observed during the download.
126    Protocol(Uuid, ProtocolInfo),
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
130#[serde(rename_all = "snake_case")]
131pub enum WorkerState {
132    Connecting,
133    WaitingForWork,
134    Downloading,
135    Retrying,
136    Paused,
137    Stopped,
138    Finished,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142#[serde(rename_all = "camelCase")]
143pub struct WorkerSnapshot {
144    pub connection_id: u32,
145    pub state: WorkerState,
146    pub transferred_bytes: u64,
147    pub speed_bps: f64,
148    pub range_start: Option<u64>,
149    pub range_end: Option<u64>,
150    pub range_cursor: Option<u64>,
151    pub detail: Option<String>,
152}
153
154pub enum EngineCommand {
155    Add(DownloadTask),
156    Resume(Uuid),
157    Stop(Uuid),
158    Cancel(Uuid),
159    UpdateScaling(Uuid, ScalerConfig),
160    RuntimeStopped(TaskSnapshot, HaltMode),
161}
162
163/// Rich protocol information: what the user requested vs what was negotiated.
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
165pub struct ProtocolInfo {
166    /// The HTTP mode that was requested (Auto, Http1, Http2, Http3).
167    pub requested: HttpMode,
168    /// The dominant protocol family actually observed/negotiated.
169    pub negotiated: ProtocolFamily,
170}
171
172impl ProtocolInfo {
173    /// Short display label (e.g. "auto→h2", "h1", "h3").
174    pub fn display_label(&self) -> String {
175        let req = match self.requested {
176            HttpMode::Auto => "auto",
177            HttpMode::Http1 => "h1",
178            HttpMode::Http2 => "h2",
179            HttpMode::Http3 => "h3",
180        };
181        let neg = self.negotiated.as_str();
182        if req == neg || self.negotiated == ProtocolFamily::Other {
183            req.to_string()
184        } else {
185            format!("{}→{}", req, neg)
186        }
187    }
188}
189
190impl Default for ProtocolInfo {
191    fn default() -> Self {
192        Self {
193            requested: HttpMode::Auto,
194            negotiated: ProtocolFamily::Other,
195        }
196    }
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
200pub enum HaltMode {
201    /// Task is actively running / scheduling work.
202    Running,
203    /// Stop issuing new work; allow workers to reach a safe relinquish boundary.
204    Draining,
205    /// Runtime remains alive, client state preserved, workers owned but idle.
206    Hibernating,
207    /// Cancel/stop path that writes resumable state to disk.
208    PersistToDisk,
209}