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}