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    /// Reused read buffer for upload chunks (same task, sequential chunks).
51    upload_chunk_buf: Arc<Mutex<Vec<u8>>>,
52    /// Task-level download file handle slot to avoid reopening per chunk.
53    download_file_slot: Arc<Mutex<Option<File>>>,
54    /// Max retries after first failed upload prepare (`BreakpointUpload::prepare`).
55    max_upload_prepare_retries: u32,
56}
57
58impl std::fmt::Debug for TransferTask {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.debug_struct("TransferTask")
61            .field("file_sign", &self.file_sign)
62            .field("file_name", &self.file_name)
63            .field("file_path", &self.file_path)
64            .field("upload_source", &self.upload_source)
65            .field("direction", &self.direction)
66            .field("total_size", &self.total_size)
67            .field("chunk_size", &self.chunk_size)
68            .field("url", &self.url)
69            .field("method", &self.method)
70            .field("headers", &self.headers)
71            .field("breakpoint_upload", &"<dyn BreakpointUpload>")
72            .field("breakpoint_download", &"<dyn BreakpointDownload>")
73            .field("breakpoint_download_http", &self.breakpoint_download_http)
74            .field(
75                "max_upload_prepare_retries",
76                &self.max_upload_prepare_retries,
77            )
78            .finish()
79    }
80}
81
82impl TransferTask {
83    /// Creates a transfer snapshot from an internal runtime task.
84    pub(crate) fn from_inner(inner: &InnerTask) -> Self {
85        Self {
86            file_sign: inner.file_sign_arc(),
87            file_name: inner.file_name_arc(),
88            file_path: inner.file_path().to_path_buf(),
89            upload_source: inner.upload_source().cloned(),
90            direction: inner.direction(),
91            total_size: inner.total_size(),
92            chunk_size: inner.chunk_size(),
93            url: inner.url().to_string(),
94            method: inner.method(),
95            headers: inner.headers().clone(),
96            breakpoint_download_http: inner.breakpoint_download_http().clone(),
97            breakpoint_upload: inner.breakpoint_upload().clone(),
98            breakpoint_download: inner.breakpoint_download().clone(),
99            http_client: inner.http_client_ref().cloned(),
100            upload_file_slot: Arc::new(Mutex::new(None)),
101            upload_chunk_buf: Arc::new(Mutex::new(Vec::new())),
102            download_file_slot: Arc::new(Mutex::new(None)),
103            max_upload_prepare_retries: inner.max_upload_prepare_retries(),
104        }
105    }
106
107    /// Returns transfer direction.
108    ///
109    /// # Examples
110    ///
111    /// ```no_run
112    /// use rusty_cat::api::TransferTask;
113    ///
114    /// fn inspect(task: &TransferTask) {
115    ///     let _ = task.direction();
116    /// }
117    /// ```
118    pub fn direction(&self) -> Direction {
119        self.direction
120    }
121
122    /// Returns total file size in bytes.
123    ///
124    /// # Examples
125    ///
126    /// ```no_run
127    /// use rusty_cat::api::TransferTask;
128    ///
129    /// fn inspect(task: &TransferTask) {
130    ///     let _ = task.total_size();
131    /// }
132    /// ```
133    pub fn total_size(&self) -> u64 {
134        self.total_size
135    }
136
137    /// Returns chunk size in bytes.
138    ///
139    /// # Examples
140    ///
141    /// ```no_run
142    /// use rusty_cat::api::TransferTask;
143    ///
144    /// fn inspect(task: &TransferTask) {
145    ///     let _ = task.chunk_size();
146    /// }
147    /// ```
148    pub fn chunk_size(&self) -> u64 {
149        self.chunk_size
150    }
151
152    /// Returns file signature.
153    ///
154    /// # Examples
155    ///
156    /// ```no_run
157    /// use rusty_cat::api::TransferTask;
158    ///
159    /// fn inspect(task: &TransferTask) {
160    ///     let _ = task.file_sign();
161    /// }
162    /// ```
163    pub fn file_sign(&self) -> &str {
164        &self.file_sign
165    }
166
167    /// Returns display file name.
168    ///
169    /// # Examples
170    ///
171    /// ```no_run
172    /// use rusty_cat::api::TransferTask;
173    ///
174    /// fn inspect(task: &TransferTask) {
175    ///     let _ = task.file_name();
176    /// }
177    /// ```
178    pub fn file_name(&self) -> &str {
179        &self.file_name
180    }
181
182    /// Returns local file path.
183    ///
184    /// # Examples
185    ///
186    /// ```no_run
187    /// use rusty_cat::api::TransferTask;
188    ///
189    /// fn inspect(task: &TransferTask) {
190    ///     let _ = task.file_path();
191    /// }
192    /// ```
193    pub fn file_path(&self) -> &Path {
194        &self.file_path
195    }
196
197    /// Returns upload source for upload tasks.
198    pub(crate) fn upload_source(&self) -> Option<&UploadSource> {
199        self.upload_source.as_ref()
200    }
201
202    /// Returns request URL.
203    ///
204    /// # Examples
205    ///
206    /// ```no_run
207    /// use rusty_cat::api::TransferTask;
208    ///
209    /// fn inspect(task: &TransferTask) {
210    ///     let _ = task.url();
211    /// }
212    /// ```
213    pub fn url(&self) -> &str {
214        &self.url
215    }
216
217    /// Returns request HTTP method.
218    ///
219    /// # Examples
220    ///
221    /// ```no_run
222    /// use rusty_cat::api::TransferTask;
223    ///
224    /// fn inspect(task: &TransferTask) {
225    ///     let _ = task.method();
226    /// }
227    /// ```
228    pub fn method(&self) -> Method {
229        self.method.clone()
230    }
231
232    /// Returns base request headers.
233    ///
234    /// # Examples
235    ///
236    /// ```no_run
237    /// use rusty_cat::api::TransferTask;
238    ///
239    /// fn inspect(task: &TransferTask) {
240    ///     let _ = task.headers();
241    /// }
242    /// ```
243    pub fn headers(&self) -> &HeaderMap {
244        &self.headers
245    }
246
247    /// Returns task-level breakpoint download HTTP configuration.
248    ///
249    /// Custom [`crate::download_trait::BreakpointDownload`] implementations can
250    /// read values such as `range_accept`.
251    ///
252    /// # Examples
253    ///
254    /// ```no_run
255    /// use rusty_cat::api::TransferTask;
256    ///
257    /// fn inspect(task: &TransferTask) {
258    ///     let _ = task.breakpoint_download_http();
259    /// }
260    /// ```
261    pub fn breakpoint_download_http(&self) -> Option<&BreakpointDownloadHttpConfig> {
262        Some(&self.breakpoint_download_http)
263    }
264
265    /// Returns task-level upload protocol implementation.
266    pub(crate) fn breakpoint_upload(&self) -> Option<&Arc<dyn BreakpointUpload + Send + Sync>> {
267        Some(&self.breakpoint_upload)
268    }
269
270    /// Returns task-level download protocol implementation.
271    pub(crate) fn breakpoint_download(&self) -> Option<&Arc<dyn BreakpointDownload + Send + Sync>> {
272        Some(&self.breakpoint_download)
273    }
274
275    /// Returns max retries after the first failed upload prepare.
276    pub(crate) fn max_upload_prepare_retries(&self) -> u32 {
277        self.max_upload_prepare_retries
278    }
279
280    /// Returns task-level custom HTTP client, if configured.
281    pub(crate) fn http_client_ref(&self) -> Option<&reqwest::Client> {
282        self.http_client.as_ref()
283    }
284
285    /// Returns upload file handle slot used by executor.
286    pub(crate) fn upload_file_slot(&self) -> &Arc<Mutex<Option<File>>> {
287        &self.upload_file_slot
288    }
289
290    /// Returns per-task upload chunk read buffer (executor-internal reuse).
291    pub(crate) fn upload_chunk_buf(&self) -> &Arc<Mutex<Vec<u8>>> {
292        &self.upload_chunk_buf
293    }
294
295    /// Returns download file handle slot used by executor.
296    pub(crate) fn download_file_slot(&self) -> &Arc<Mutex<Option<File>>> {
297        &self.download_file_slot
298    }
299}