Skip to main content

rusty_cat/
pounce_task.rs

1use std::fmt;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use reqwest::header::HeaderMap;
6use reqwest::Method;
7
8use crate::direction::Direction;
9use crate::http_breakpoint::{BreakpointDownload, BreakpointDownloadHttpConfig, BreakpointUpload};
10
11/// User-facing task input built by upload/download builders.
12///
13/// This type only carries request parameters. Internal runtime state is created
14/// later when the task is enqueued.
15#[derive(Clone)]
16pub struct PounceTask {
17    /// Transfer direction.
18    pub(crate) direction: Direction,
19    /// Display file name.
20    pub(crate) file_name: String,
21    /// Local source/target path.
22    pub(crate) file_path: PathBuf,
23    /// Total file size in bytes (upload only at build time).
24    pub(crate) total_size: u64,
25    /// Chunk size in bytes.
26    pub(crate) chunk_size: u64,
27    /// Request URL.
28    pub(crate) url: String,
29    /// Request HTTP method.
30    pub(crate) method: Method,
31    /// Base request headers.
32    pub(crate) headers: HeaderMap,
33    /// Download-only signature shown in callbacks.
34    ///
35    /// Upload tasks ignore this value and use internal signature generation.
36    pub(crate) client_file_sign: Option<String>,
37    /// Optional custom upload breakpoint protocol.
38    pub(crate) breakpoint_upload: Option<Arc<dyn BreakpointUpload + Send + Sync>>,
39    /// Optional custom download breakpoint protocol.
40    pub(crate) breakpoint_download: Option<Arc<dyn BreakpointDownload + Send + Sync>>,
41    /// Optional HTTP configuration for breakpoint download.
42    pub(crate) breakpoint_download_http: Option<BreakpointDownloadHttpConfig>,
43    /// Maximum retry count per chunk transfer.
44    ///
45    /// Applies only to chunk transfer stage, not prepare stage.
46    pub(crate) max_chunk_retries: u32,
47}
48
49impl fmt::Debug for PounceTask {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        f.debug_struct("PounceTask")
52            .field("direction", &self.direction)
53            .field("file_name", &self.file_name)
54            .field("file_path", &self.file_path)
55            .field("total_size", &self.total_size)
56            .field("chunk_size", &self.chunk_size)
57            .field("url", &self.url)
58            .field("method", &self.method)
59            .field("headers", &self.headers)
60            .field("client_file_sign", &self.client_file_sign)
61            .field(
62                "breakpoint_upload",
63                &self
64                    .breakpoint_upload
65                    .as_ref()
66                    .map(|_| "Arc<dyn BreakpointUpload + Send + Sync>"),
67            )
68            .field(
69                "breakpoint_download",
70                &self
71                    .breakpoint_download
72                    .as_ref()
73                    .map(|_| "Arc<dyn BreakpointDownload + Send + Sync>"),
74            )
75            .field("breakpoint_download_http", &self.breakpoint_download_http)
76            .field("max_chunk_retries", &self.max_chunk_retries)
77            .finish()
78    }
79}
80
81impl PounceTask {
82    /// Default maximum retry count per chunk transfer.
83    pub const DEFAULT_MAX_CHUNK_RETRIES: u32 = 3;
84
85    /// Normalizes chunk size input.
86    ///
87    /// `0` is converted to `1 MiB`; other values are kept unchanged.
88    pub(crate) fn normalized_chunk_size(chunk_size: u64) -> u64 {
89        if chunk_size == 0 {
90            1024 * 1024
91        } else {
92            chunk_size
93        }
94    }
95
96    /// Normalizes retry count input.
97    ///
98    /// - `0` means "disable retry".
99    /// - Other values are used as-is.
100    pub(crate) fn normalized_max_chunk_retries(max_chunk_retries: u32) -> u32 {
101        max_chunk_retries
102    }
103
104    /// Checks whether required task fields are missing/invalid.
105    ///
106    /// For upload, `total_size` must be greater than `0`.
107    pub(crate) fn is_empty(&self) -> bool {
108        self.file_path.as_os_str().is_empty()
109            || self.file_name.is_empty()
110            || self.url.is_empty()
111            || match self.direction {
112                Direction::Upload => self.total_size == 0,
113                Direction::Download => false,
114            }
115    }
116}