ossify/ops/object/multipart_upload/
upload_part_copy.rs1use std::future::Future;
2
3use http::{HeaderMap, HeaderName, Method};
4use serde::{Deserialize, Serialize};
5
6use crate::body::ZeroBody;
7use crate::error::Result;
8use crate::response::BodyResponseProcessor;
9use crate::{Client, Ops, Prepared, Request, escape_path};
10
11#[derive(Debug, Clone, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct UploadPartCopyParams {
15 pub part_number: u32,
16 pub upload_id: String,
17}
18
19impl UploadPartCopyParams {
20 pub fn new(part_number: u32, upload_id: impl Into<String>) -> Self {
21 Self {
22 part_number,
23 upload_id: upload_id.into(),
24 }
25 }
26}
27
28#[derive(Debug, Clone, Default)]
30pub struct UploadPartCopyOptions {
31 pub copy_source_range: Option<(u64, u64)>,
33 pub copy_source_if_match: Option<String>,
35 pub copy_source_if_none_match: Option<String>,
37 pub copy_source_if_unmodified_since: Option<String>,
39 pub copy_source_if_modified_since: Option<String>,
41}
42
43impl UploadPartCopyOptions {
44 fn into_headers(
45 self,
46 source_bucket: String,
47 source_key: String,
48 source_version_id: Option<String>,
49 ) -> Result<HeaderMap> {
50 let mut headers = HeaderMap::new();
51
52 let source_key = escape_path(&source_key);
53 let mut source_str = format!("/{source_bucket}/{source_key}");
54 if let Some(source_version_id) = source_version_id {
55 source_str.push_str(&format!("?versionId={source_version_id}"));
56 }
57 headers.insert(HeaderName::from_static("x-oss-copy-source"), source_str.parse()?);
58
59 if let Some(ref copy_source_range) = self.copy_source_range {
60 headers.insert(
61 HeaderName::from_static("x-oss-copy-source-range"),
62 format!("bytes={}-{}", copy_source_range.0, copy_source_range.1).parse()?,
63 );
64 }
65
66 if let Some(ref copy_source_if_match) = self.copy_source_if_match {
67 headers.insert(
68 HeaderName::from_static("x-oss-copy-source-if-match"),
69 copy_source_if_match.to_string().parse()?,
70 );
71 }
72
73 if let Some(ref copy_source_if_none_match) = self.copy_source_if_none_match {
74 headers.insert(
75 HeaderName::from_static("x-oss-copy-source-if-none-match"),
76 copy_source_if_none_match.to_string().parse()?,
77 );
78 }
79
80 if let Some(ref copy_source_if_modified_since) = self.copy_source_if_modified_since {
81 headers.insert(
82 HeaderName::from_static("x-oss-copy-source-if-modified-since"),
83 copy_source_if_modified_since.to_string().parse()?,
84 );
85 }
86
87 if let Some(ref copy_source_if_unmodified_since) = self.copy_source_if_unmodified_since {
88 headers.insert(
89 HeaderName::from_static("x-oss-copy-source-if-unmodified-since"),
90 copy_source_if_unmodified_since.to_string().parse()?,
91 );
92 }
93
94 Ok(headers)
95 }
96}
97
98#[derive(Debug, Clone, Deserialize)]
100#[serde(rename_all = "PascalCase")]
101pub struct CopyPartResult {
102 pub etag: String,
104 pub last_modified: String,
106}
107
108#[derive(Debug, Clone, Deserialize)]
110pub struct UploadPartCopyResult {
111 pub copy_part_result: CopyPartResult,
113 pub part_number: u32,
115}
116
117pub struct UploadPartCopy {
119 pub object_key: String,
120 pub source_bucket: String,
121 pub source_key: String,
122 pub source_version_id: Option<String>,
123 pub params: UploadPartCopyParams,
124 pub options: UploadPartCopyOptions,
125}
126
127impl Ops for UploadPartCopy {
128 type Response = BodyResponseProcessor<UploadPartCopyResult>;
129 type Body = ZeroBody;
130 type Query = UploadPartCopyParams;
131
132 fn prepare(self) -> Result<Prepared<UploadPartCopyParams>> {
133 Ok(Prepared {
134 method: Method::PUT,
135 key: Some(self.object_key),
136 query: Some(self.params),
137 headers: Some(self.options.into_headers(
138 self.source_bucket,
139 self.source_key,
140 self.source_version_id,
141 )?),
142 body: Some(()),
143 ..Default::default()
144 })
145 }
146}
147
148pub trait UploadPartCopyOperations {
150 #[allow(clippy::too_many_arguments)]
154 fn upload_part_copy(
155 &self,
156 object_key: impl Into<String>,
157 upload_id: impl Into<String>,
158 part_number: u32,
159 source_bucket: impl Into<String>,
160 source_key: impl Into<String>,
161 options: Option<UploadPartCopyOptions>,
162 ) -> impl Future<Output = Result<UploadPartCopyResult>>;
163
164 #[allow(clippy::too_many_arguments)]
168 fn upload_part_copy_with_version_id(
169 &self,
170 object_key: impl Into<String>,
171 upload_id: impl Into<String>,
172 part_number: u32,
173 source_bucket: impl Into<String>,
174 source_key: impl Into<String>,
175 source_version_id: impl Into<String>,
176 options: Option<UploadPartCopyOptions>,
177 ) -> impl Future<Output = Result<UploadPartCopyResult>>;
178}
179
180impl UploadPartCopyOperations for Client {
181 async fn upload_part_copy(
182 &self,
183 object_key: impl Into<String>,
184 upload_id: impl Into<String>,
185 part_number: u32,
186 source_bucket: impl Into<String>,
187 source_key: impl Into<String>,
188 options: Option<UploadPartCopyOptions>,
189 ) -> Result<UploadPartCopyResult> {
190 let ops = UploadPartCopy {
191 object_key: object_key.into(),
192 source_bucket: source_bucket.into(),
193 source_key: source_key.into(),
194 source_version_id: None,
195 params: UploadPartCopyParams::new(part_number, upload_id),
196 options: options.unwrap_or_default(),
197 };
198 self.request(ops).await
199 }
200
201 async fn upload_part_copy_with_version_id(
202 &self,
203 object_key: impl Into<String>,
204 upload_id: impl Into<String>,
205 part_number: u32,
206 source_bucket: impl Into<String>,
207 source_key: impl Into<String>,
208 source_version_id: impl Into<String>,
209 options: Option<UploadPartCopyOptions>,
210 ) -> Result<UploadPartCopyResult> {
211 let ops = UploadPartCopy {
212 object_key: object_key.into(),
213 source_bucket: source_bucket.into(),
214 source_key: source_key.into(),
215 source_version_id: Some(source_version_id.into()),
216 params: UploadPartCopyParams::new(part_number, upload_id),
217 options: options.unwrap_or_default(),
218 };
219 self.request(ops).await
220 }
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
225#[serde(rename_all = "PascalCase")]
226pub struct PartCopyInfo {
227 pub part_number: u32,
228 pub etag: String,
229}
230
231impl PartCopyInfo {
232 pub fn new(part_number: u32, etag: impl Into<String>) -> Self {
233 Self {
234 part_number,
235 etag: etag.into(),
236 }
237 }
238}
239
240impl From<UploadPartCopyResult> for PartCopyInfo {
241 fn from(result: UploadPartCopyResult) -> Self {
242 Self {
243 part_number: result.part_number,
244 etag: result.copy_part_result.etag,
245 }
246 }
247}