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}