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