ossify/ops/object/multipart_upload/
upload_part_copy.rs

1use 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/// UploadPartCopy request parameters
12#[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/// UploadPartCopy request options
29#[derive(Debug, Clone, Default)]
30pub struct UploadPartCopyOptions {
31    /// Specify the range of the copy source object
32    pub copy_source_range: Option<(u64, u64)>,
33    /// Copy condition for source object: execute copy operation if source object's ETag equals the user-provided ETag
34    pub copy_source_if_match: Option<String>,
35    /// Copy condition for source object: execute copy operation if source object's ETag does not equal the user-provided ETag
36    pub copy_source_if_none_match: Option<String>,
37    /// Copy condition for source object: transfer file normally if the time in the parameter is equal to or later than the actual file modification time
38    pub copy_source_if_unmodified_since: Option<String>,
39    /// Copy condition for source object: execute copy operation if source object was modified after the user-specified time
40    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/// Copy result in UploadPartCopy response
99#[derive(Debug, Clone, Deserialize)]
100#[serde(rename_all = "PascalCase")]
101pub struct CopyPartResult {
102    /// ETag value of the newly created object
103    pub etag: String,
104    /// Last modification time of the object
105    pub last_modified: String,
106}
107
108/// UploadPartCopy response
109#[derive(Debug, Clone, Deserialize)]
110pub struct UploadPartCopyResult {
111    /// Copy result
112    pub copy_part_result: CopyPartResult,
113    /// Part number
114    pub part_number: u32,
115}
116
117/// UploadPartCopy operation
118pub 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
148/// Trait for UploadPartCopy operations
149pub trait UploadPartCopyOperations {
150    /// Upload part copy
151    ///
152    /// Official documentation: <https://www.alibabacloud.com/help/en/oss/developer-reference/uploadpartcopy>
153    #[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    /// Upload part copy
165    ///
166    /// Official documentation: <https://www.alibabacloud.com/help/en/oss/developer-reference/uploadpartcopy>
167    #[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/// Part copy information, used for completing multipart upload
224#[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}