rusty_cat/transfer_task.rs
1use std::path::{Path, PathBuf};
2use std::sync::Arc;
3
4use reqwest::header::HeaderMap;
5use reqwest::Method;
6use tokio::fs::File;
7use tokio::sync::Mutex;
8
9use crate::direction::Direction;
10use crate::http_breakpoint::{BreakpointDownload, BreakpointDownloadHttpConfig, BreakpointUpload};
11use crate::inner::inner_task::InnerTask;
12use crate::upload_source::UploadSource;
13
14/// Immutable task snapshot exposed to transfer executor implementations.
15///
16/// This type is constructed from the crate-internal scheduler task state and
17/// intentionally exposes read-only accessors.
18#[derive(Clone)]
19pub struct TransferTask {
20 /// Stable file signature.
21 file_sign: Arc<str>,
22 /// Display file name.
23 file_name: Arc<str>,
24 /// Local file path.
25 file_path: PathBuf,
26 /// Upload-only source descriptor.
27 upload_source: Option<UploadSource>,
28 /// Transfer direction.
29 direction: Direction,
30 /// Total file size in bytes.
31 total_size: u64,
32 /// Chunk size in bytes.
33 chunk_size: u64,
34 /// Request URL.
35 url: String,
36 /// Request HTTP method.
37 method: Method,
38 /// Base request headers.
39 headers: HeaderMap,
40 /// HTTP config for breakpoint download behavior.
41 breakpoint_download_http: BreakpointDownloadHttpConfig,
42 /// Upload breakpoint protocol implementation.
43 breakpoint_upload: Arc<dyn BreakpointUpload + Send + Sync>,
44 /// Download breakpoint protocol implementation.
45 breakpoint_download: Arc<dyn BreakpointDownload + Send + Sync>,
46 /// Optional per-task custom HTTP client.
47 http_client: Option<reqwest::Client>,
48 /// Task-level upload file handle slot to avoid reopening per chunk.
49 upload_file_slot: Arc<Mutex<Option<File>>>,
50 /// Task-level download file handle slot to avoid reopening per chunk.
51 download_file_slot: Arc<Mutex<Option<File>>>,
52 /// Max parts of this file transferred concurrently (intra-file parallel).
53 /// `1` means the strict-serial legacy path.
54 max_parts_in_flight: usize,
55 /// Shared progress bitmap for the concurrent download path (None until the
56 /// parallel `download_prepare` initializes it). Guarded so concurrent parts
57 /// can flip their bit without racing.
58 download_progress: Arc<Mutex<Option<crate::dflt::download_progress::DownloadProgress>>>,
59 /// Max retries after first failed upload prepare (`BreakpointUpload::prepare`).
60 max_upload_prepare_retries: u32,
61}
62
63impl std::fmt::Debug for TransferTask {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 f.debug_struct("TransferTask")
66 .field("file_sign", &self.file_sign)
67 .field("file_name", &self.file_name)
68 .field("file_path", &self.file_path)
69 .field("upload_source", &self.upload_source)
70 .field("direction", &self.direction)
71 .field("total_size", &self.total_size)
72 .field("chunk_size", &self.chunk_size)
73 .field("url", &self.url)
74 .field("method", &self.method)
75 .field("headers", &self.headers)
76 .field("breakpoint_upload", &"<dyn BreakpointUpload>")
77 .field("breakpoint_download", &"<dyn BreakpointDownload>")
78 .field("breakpoint_download_http", &self.breakpoint_download_http)
79 .field(
80 "max_upload_prepare_retries",
81 &self.max_upload_prepare_retries,
82 )
83 .finish()
84 }
85}
86
87impl TransferTask {
88 /// Creates a transfer snapshot from an internal runtime task.
89 pub(crate) fn from_inner(inner: &InnerTask) -> Self {
90 Self {
91 file_sign: inner.file_sign_arc(),
92 file_name: inner.file_name_arc(),
93 file_path: inner.file_path().to_path_buf(),
94 upload_source: inner.upload_source().cloned(),
95 direction: inner.direction(),
96 total_size: inner.total_size(),
97 chunk_size: inner.chunk_size(),
98 url: inner.url().to_string(),
99 method: inner.method(),
100 headers: inner.headers().clone(),
101 breakpoint_download_http: inner.breakpoint_download_http().clone(),
102 breakpoint_upload: inner.breakpoint_upload().clone(),
103 breakpoint_download: inner.breakpoint_download().clone(),
104 http_client: inner.http_client_ref().cloned(),
105 upload_file_slot: Arc::new(Mutex::new(None)),
106 download_file_slot: Arc::new(Mutex::new(None)),
107 max_parts_in_flight: inner.max_parts_in_flight(),
108 download_progress: Arc::new(Mutex::new(None)),
109 max_upload_prepare_retries: inner.max_upload_prepare_retries(),
110 }
111 }
112
113 /// Returns transfer direction.
114 ///
115 /// # Examples
116 ///
117 /// ```no_run
118 /// use rusty_cat::api::TransferTask;
119 ///
120 /// fn inspect(task: &TransferTask) {
121 /// let _ = task.direction();
122 /// }
123 /// ```
124 pub fn direction(&self) -> Direction {
125 self.direction
126 }
127
128 /// Returns total file size in bytes.
129 ///
130 /// # Examples
131 ///
132 /// ```no_run
133 /// use rusty_cat::api::TransferTask;
134 ///
135 /// fn inspect(task: &TransferTask) {
136 /// let _ = task.total_size();
137 /// }
138 /// ```
139 pub fn total_size(&self) -> u64 {
140 self.total_size
141 }
142
143 /// Returns chunk size in bytes.
144 ///
145 /// # Examples
146 ///
147 /// ```no_run
148 /// use rusty_cat::api::TransferTask;
149 ///
150 /// fn inspect(task: &TransferTask) {
151 /// let _ = task.chunk_size();
152 /// }
153 /// ```
154 pub fn chunk_size(&self) -> u64 {
155 self.chunk_size
156 }
157
158 /// Returns file signature.
159 ///
160 /// # Examples
161 ///
162 /// ```no_run
163 /// use rusty_cat::api::TransferTask;
164 ///
165 /// fn inspect(task: &TransferTask) {
166 /// let _ = task.file_sign();
167 /// }
168 /// ```
169 pub fn file_sign(&self) -> &str {
170 &self.file_sign
171 }
172
173 /// Returns display file name.
174 ///
175 /// # Examples
176 ///
177 /// ```no_run
178 /// use rusty_cat::api::TransferTask;
179 ///
180 /// fn inspect(task: &TransferTask) {
181 /// let _ = task.file_name();
182 /// }
183 /// ```
184 pub fn file_name(&self) -> &str {
185 &self.file_name
186 }
187
188 /// Returns local file path.
189 ///
190 /// # Examples
191 ///
192 /// ```no_run
193 /// use rusty_cat::api::TransferTask;
194 ///
195 /// fn inspect(task: &TransferTask) {
196 /// let _ = task.file_path();
197 /// }
198 /// ```
199 pub fn file_path(&self) -> &Path {
200 &self.file_path
201 }
202
203 /// Returns upload source for upload tasks.
204 pub(crate) fn upload_source(&self) -> Option<&UploadSource> {
205 self.upload_source.as_ref()
206 }
207
208 /// Returns request URL.
209 ///
210 /// # Examples
211 ///
212 /// ```no_run
213 /// use rusty_cat::api::TransferTask;
214 ///
215 /// fn inspect(task: &TransferTask) {
216 /// let _ = task.url();
217 /// }
218 /// ```
219 pub fn url(&self) -> &str {
220 &self.url
221 }
222
223 /// Returns request HTTP method.
224 ///
225 /// # Examples
226 ///
227 /// ```no_run
228 /// use rusty_cat::api::TransferTask;
229 ///
230 /// fn inspect(task: &TransferTask) {
231 /// let _ = task.method();
232 /// }
233 /// ```
234 pub fn method(&self) -> Method {
235 self.method.clone()
236 }
237
238 /// Returns base request headers.
239 ///
240 /// # Examples
241 ///
242 /// ```no_run
243 /// use rusty_cat::api::TransferTask;
244 ///
245 /// fn inspect(task: &TransferTask) {
246 /// let _ = task.headers();
247 /// }
248 /// ```
249 pub fn headers(&self) -> &HeaderMap {
250 &self.headers
251 }
252
253 /// Returns task-level breakpoint download HTTP configuration.
254 ///
255 /// Custom [`crate::download_trait::BreakpointDownload`] implementations can
256 /// read values such as `range_accept`.
257 ///
258 /// # Examples
259 ///
260 /// ```no_run
261 /// use rusty_cat::api::TransferTask;
262 ///
263 /// fn inspect(task: &TransferTask) {
264 /// let _ = task.breakpoint_download_http();
265 /// }
266 /// ```
267 pub fn breakpoint_download_http(&self) -> Option<&BreakpointDownloadHttpConfig> {
268 Some(&self.breakpoint_download_http)
269 }
270
271 /// Returns task-level upload protocol implementation.
272 pub(crate) fn breakpoint_upload(&self) -> Option<&Arc<dyn BreakpointUpload + Send + Sync>> {
273 Some(&self.breakpoint_upload)
274 }
275
276 /// Returns task-level download protocol implementation.
277 pub(crate) fn breakpoint_download(&self) -> Option<&Arc<dyn BreakpointDownload + Send + Sync>> {
278 Some(&self.breakpoint_download)
279 }
280
281 /// Returns max retries after the first failed upload prepare.
282 pub(crate) fn max_upload_prepare_retries(&self) -> u32 {
283 self.max_upload_prepare_retries
284 }
285
286 /// Returns task-level custom HTTP client, if configured.
287 pub(crate) fn http_client_ref(&self) -> Option<&reqwest::Client> {
288 self.http_client.as_ref()
289 }
290
291 /// Returns upload file handle slot used by executor.
292 pub(crate) fn upload_file_slot(&self) -> &Arc<Mutex<Option<File>>> {
293 &self.upload_file_slot
294 }
295
296 /// Returns download file handle slot used by executor.
297 pub(crate) fn download_file_slot(&self) -> &Arc<Mutex<Option<File>>> {
298 &self.download_file_slot
299 }
300
301 /// Returns the configured max concurrent parts for this task.
302 pub(crate) fn max_parts_in_flight(&self) -> usize {
303 self.max_parts_in_flight
304 }
305
306 /// Returns the shared concurrent-download progress slot.
307 pub(crate) fn download_progress(
308 &self,
309 ) -> &Arc<Mutex<Option<crate::dflt::download_progress::DownloadProgress>>> {
310 &self.download_progress
311 }
312}