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}