Skip to main content

rusty_cat/
upload_trait.rs

1use crate::http_breakpoint::UploadResumeInfo;
2use crate::{MeowError, TransferTask};
3use async_trait::async_trait;
4
5/// Context for upload prepare stage.
6#[derive(Debug, Clone, Copy)]
7pub struct UploadPrepareCtx<'a> {
8    /// HTTP client used for requests.
9    pub client: &'a reqwest::Client,
10    /// Immutable task snapshot.
11    pub task: &'a TransferTask,
12    /// Locally confirmed uploaded offset in bytes.
13    ///
14    /// Range: `>= 0`.
15    pub local_offset: u64,
16}
17
18/// Context for upload chunk stage.
19#[derive(Debug, Clone, Copy)]
20pub struct UploadChunkCtx<'a> {
21    /// HTTP client used for requests.
22    pub client: &'a reqwest::Client,
23    /// Immutable task snapshot.
24    pub task: &'a TransferTask,
25    /// Raw bytes for the current chunk.
26    pub chunk: &'a [u8],
27    /// Start offset of this chunk in the full file.
28    ///
29    /// Range: `>= 0`.
30    pub offset: u64,
31}
32
33/// Custom breakpoint upload protocol.
34///
35/// Implementors are responsible for request construction and response parsing.
36/// The executor handles file I/O, chunking, retries, progress, and scheduling.
37///
38/// # Examples
39///
40/// ```no_run
41/// use async_trait::async_trait;
42/// use rusty_cat::api::{
43///     BreakpointUpload, MeowError, UploadChunkCtx, UploadPrepareCtx, UploadResumeInfo,
44/// };
45///
46/// struct MyUploadProtocol;
47///
48/// #[async_trait]
49/// impl BreakpointUpload for MyUploadProtocol {
50///     async fn prepare(&self, _ctx: UploadPrepareCtx<'_>) -> Result<UploadResumeInfo, MeowError> {
51///         Ok(UploadResumeInfo::default())
52///     }
53///
54///     async fn upload_chunk(&self, ctx: UploadChunkCtx<'_>) -> Result<UploadResumeInfo, MeowError> {
55///         let _ = (ctx.task.file_name(), ctx.offset);
56///         Ok(UploadResumeInfo {
57///             completed_file_id: None,
58///             next_byte: Some(ctx.offset + ctx.chunk.len() as u64),
59///         })
60///     }
61/// }
62/// ```
63///
64/// # Executor integration contract
65///
66/// - If [`UploadResumeInfo::completed_file_id`] is `Some`, the executor treats
67///   the upload as fully completed and stops sending further chunks.
68/// - [`UploadResumeInfo::next_byte`] is a server-suggested next offset; the
69///   executor merges it with local offset before continuing.
70/// - When computed next offset reaches `task.total_size()`, the executor calls
71///   [`BreakpointUpload::complete_upload`] unless completion was already
72///   indicated by `completed_file_id`.
73/// - When user cancels an upload task, executor calls
74///   [`BreakpointUpload::abort_upload`].
75#[async_trait]
76pub trait BreakpointUpload: Send + Sync {
77    /// Prepare stage before first chunk upload.
78    ///
79    /// Typical responsibilities include creating upload session and querying
80    /// already uploaded offset on remote side.
81    ///
82    /// # Parameters
83    ///
84    /// - `client`: Shared HTTP client used by executor.
85    /// - `task`: Upload task snapshot with URL/method/headers/file metadata.
86    /// - `local_offset`: Locally confirmed uploaded offset.
87    ///
88    /// # Returns
89    ///
90    /// - `Ok(info)`: Server resume info used by executor to compute next offset.
91    /// - `Err`: Prepare failed and task enters error path.
92    ///
93    /// # Errors
94    ///
95    /// Return [`MeowError`] when remote session creation/checkpoint probing
96    /// fails, request signing fails, or protocol payload parsing fails.
97    ///
98    /// # Examples
99    ///
100    /// ```no_run
101    /// use rusty_cat::api::UploadPrepareCtx;
102    ///
103    /// fn read_prepare_ctx(ctx: UploadPrepareCtx<'_>) {
104    ///     let _ = (ctx.task.url(), ctx.local_offset);
105    /// }
106    /// ```
107    async fn prepare(&self, ctx: UploadPrepareCtx<'_>) -> Result<UploadResumeInfo, MeowError>;
108
109    /// Uploads a single chunk.
110    ///
111    /// Executor guarantees chunk bounds are valid and chunk bytes match the
112    /// provided `offset`.
113    ///
114    /// # Errors
115    ///
116    /// Return [`MeowError`] when chunk request fails, server rejects the chunk,
117    /// or protocol response parsing fails.
118    ///
119    /// # Examples
120    ///
121    /// ```no_run
122    /// use rusty_cat::api::UploadChunkCtx;
123    ///
124    /// fn read_chunk_ctx(ctx: UploadChunkCtx<'_>) {
125    ///     let _ = (ctx.offset, ctx.chunk.len(), ctx.task.total_size());
126    /// }
127    /// ```
128    async fn upload_chunk(&self, ctx: UploadChunkCtx<'_>) -> Result<UploadResumeInfo, MeowError>;
129
130    /// Finalization step after all chunk bytes are uploaded.
131    ///
132    /// Typical use case: multipart-complete API calls. Return value is an
133    /// optional provider-defined payload that will be forwarded to
134    /// `MeowClient::enqueue` complete callback.
135    ///
136    /// Default implementation is a no-op (`Ok(None)`).
137    ///
138    /// # Errors
139    ///
140    /// Implementations should return [`MeowError`] if final commit/merge API
141    /// fails.
142    async fn complete_upload(
143        &self,
144        _client: &reqwest::Client,
145        _task: &TransferTask,
146    ) -> Result<Option<String>, MeowError> {
147        Ok(None)
148    }
149
150    /// Abort/cleanup hook called when user cancels an upload task.
151    ///
152    /// Typical use case: abort multipart session or remove temporary objects.
153    /// Default implementation is a no-op.
154    ///
155    /// # Errors
156    ///
157    /// Implementations should return [`MeowError`] when cleanup or abort API
158    /// calls fail.
159    async fn abort_upload(
160        &self,
161        _client: &reqwest::Client,
162        _task: &TransferTask,
163    ) -> Result<(), MeowError> {
164        Ok(())
165    }
166}