Skip to main content

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}