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;
12
13/// Immutable task snapshot exposed to transfer executor implementations.
14///
15/// This type is internally created from [`InnerTask`] and intentionally exposes
16/// read-only accessors.
17#[derive(Clone)]
18pub struct TransferTask {
19 /// Stable file signature.
20 file_sign: String,
21 /// Display file name.
22 file_name: String,
23 /// Local file path.
24 file_path: PathBuf,
25 /// Transfer direction.
26 direction: Direction,
27 /// Total file size in bytes.
28 total_size: u64,
29 /// Chunk size in bytes.
30 chunk_size: u64,
31 /// Request URL.
32 url: String,
33 /// Request HTTP method.
34 method: Method,
35 /// Base request headers.
36 headers: HeaderMap,
37 /// HTTP config for breakpoint download behavior.
38 breakpoint_download_http: BreakpointDownloadHttpConfig,
39 /// Upload breakpoint protocol implementation.
40 breakpoint_upload: Arc<dyn BreakpointUpload + Send + Sync>,
41 /// Download breakpoint protocol implementation.
42 breakpoint_download: Arc<dyn BreakpointDownload + Send + Sync>,
43 /// Optional per-task custom HTTP client.
44 http_client: Option<reqwest::Client>,
45 /// Task-level upload file handle slot to avoid reopening per chunk.
46 upload_file_slot: Arc<Mutex<Option<File>>>,
47 /// Task-level download file handle slot to avoid reopening per chunk.
48 download_file_slot: Arc<Mutex<Option<File>>>,
49}
50
51impl std::fmt::Debug for TransferTask {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("TransferTask")
54 .field("file_sign", &self.file_sign)
55 .field("file_name", &self.file_name)
56 .field("file_path", &self.file_path)
57 .field("direction", &self.direction)
58 .field("total_size", &self.total_size)
59 .field("chunk_size", &self.chunk_size)
60 .field("url", &self.url)
61 .field("method", &self.method)
62 .field("headers", &self.headers)
63 .field("breakpoint_upload", &"<dyn BreakpointUpload>")
64 .field("breakpoint_download", &"<dyn BreakpointDownload>")
65 .field("breakpoint_download_http", &self.breakpoint_download_http)
66 .finish()
67 }
68}
69
70impl TransferTask {
71 /// Creates a transfer snapshot from an internal runtime task.
72 pub(crate) fn from_inner(inner: &InnerTask) -> Self {
73 Self {
74 file_sign: inner.file_sign().to_string(),
75 file_name: inner.file_name().to_string(),
76 file_path: inner.file_path().to_path_buf(),
77 direction: inner.direction(),
78 total_size: inner.total_size(),
79 chunk_size: inner.chunk_size(),
80 url: inner.url().to_string(),
81 method: inner.method(),
82 headers: inner.headers().clone(),
83 breakpoint_download_http: inner.breakpoint_download_http().clone(),
84 breakpoint_upload: inner.breakpoint_upload().clone(),
85 breakpoint_download: inner.breakpoint_download().clone(),
86 http_client: inner.http_client_ref().cloned(),
87 upload_file_slot: Arc::new(Mutex::new(None)),
88 download_file_slot: Arc::new(Mutex::new(None)),
89 }
90 }
91
92 /// Returns transfer direction.
93 ///
94 /// # Examples
95 ///
96 /// ```no_run
97 /// use rusty_cat::api::TransferTask;
98 ///
99 /// fn inspect(task: &TransferTask) {
100 /// let _ = task.direction();
101 /// }
102 /// ```
103 pub fn direction(&self) -> Direction {
104 self.direction
105 }
106
107 /// Returns total file size in bytes.
108 ///
109 /// # Examples
110 ///
111 /// ```no_run
112 /// use rusty_cat::api::TransferTask;
113 ///
114 /// fn inspect(task: &TransferTask) {
115 /// let _ = task.total_size();
116 /// }
117 /// ```
118 pub fn total_size(&self) -> u64 {
119 self.total_size
120 }
121
122 /// Returns chunk 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.chunk_size();
131 /// }
132 /// ```
133 pub fn chunk_size(&self) -> u64 {
134 self.chunk_size
135 }
136
137 /// Returns file signature.
138 ///
139 /// # Examples
140 ///
141 /// ```no_run
142 /// use rusty_cat::api::TransferTask;
143 ///
144 /// fn inspect(task: &TransferTask) {
145 /// let _ = task.file_sign();
146 /// }
147 /// ```
148 pub fn file_sign(&self) -> &str {
149 &self.file_sign
150 }
151
152 /// Returns display file name.
153 ///
154 /// # Examples
155 ///
156 /// ```no_run
157 /// use rusty_cat::api::TransferTask;
158 ///
159 /// fn inspect(task: &TransferTask) {
160 /// let _ = task.file_name();
161 /// }
162 /// ```
163 pub fn file_name(&self) -> &str {
164 &self.file_name
165 }
166
167 /// Returns local file path.
168 ///
169 /// # Examples
170 ///
171 /// ```no_run
172 /// use rusty_cat::api::TransferTask;
173 ///
174 /// fn inspect(task: &TransferTask) {
175 /// let _ = task.file_path();
176 /// }
177 /// ```
178 pub fn file_path(&self) -> &Path {
179 &self.file_path
180 }
181
182 /// Returns request URL.
183 ///
184 /// # Examples
185 ///
186 /// ```no_run
187 /// use rusty_cat::api::TransferTask;
188 ///
189 /// fn inspect(task: &TransferTask) {
190 /// let _ = task.url();
191 /// }
192 /// ```
193 pub fn url(&self) -> &str {
194 &self.url
195 }
196
197 /// Returns request HTTP method.
198 ///
199 /// # Examples
200 ///
201 /// ```no_run
202 /// use rusty_cat::api::TransferTask;
203 ///
204 /// fn inspect(task: &TransferTask) {
205 /// let _ = task.method();
206 /// }
207 /// ```
208 pub fn method(&self) -> Method {
209 self.method.clone()
210 }
211
212 /// Returns base request headers.
213 ///
214 /// # Examples
215 ///
216 /// ```no_run
217 /// use rusty_cat::api::TransferTask;
218 ///
219 /// fn inspect(task: &TransferTask) {
220 /// let _ = task.headers();
221 /// }
222 /// ```
223 pub fn headers(&self) -> &HeaderMap {
224 &self.headers
225 }
226
227 /// Returns task-level breakpoint download HTTP configuration.
228 ///
229 /// Custom [`crate::download_trait::BreakpointDownload`] implementations can
230 /// read values such as `range_accept`.
231 ///
232 /// # Examples
233 ///
234 /// ```no_run
235 /// use rusty_cat::api::TransferTask;
236 ///
237 /// fn inspect(task: &TransferTask) {
238 /// let _ = task.breakpoint_download_http();
239 /// }
240 /// ```
241 pub fn breakpoint_download_http(&self) -> Option<&BreakpointDownloadHttpConfig> {
242 Some(&self.breakpoint_download_http)
243 }
244
245 /// Returns task-level upload protocol implementation.
246 pub(crate) fn breakpoint_upload(&self) -> Option<&Arc<dyn BreakpointUpload + Send + Sync>> {
247 Some(&self.breakpoint_upload)
248 }
249
250 /// Returns task-level download protocol implementation.
251 pub(crate) fn breakpoint_download(&self) -> Option<&Arc<dyn BreakpointDownload + Send + Sync>> {
252 Some(&self.breakpoint_download)
253 }
254
255 /// Returns task-level custom HTTP client, if configured.
256 pub(crate) fn http_client_ref(&self) -> Option<&reqwest::Client> {
257 self.http_client.as_ref()
258 }
259
260 /// Returns upload file handle slot used by executor.
261 pub(crate) fn upload_file_slot(&self) -> &Arc<Mutex<Option<File>>> {
262 &self.upload_file_slot
263 }
264
265 /// Returns download file handle slot used by executor.
266 pub(crate) fn download_file_slot(&self) -> &Arc<Mutex<Option<File>>> {
267 &self.download_file_slot
268 }
269}