aliyun_oss_rs/object/
copy_object.rs

1use crate::{
2    common::{Acl, StorageClass, format_gmt, invalid_metadata_key, url_encode},
3    error::{Error, normal_error},
4    request::{Oss, OssRequest},
5};
6use http::Method;
7use std::collections::HashMap;
8use time::OffsetDateTime;
9
10/// Copy an object
11///
12/// Copy within the same bucket is limited to 5GB; copying across buckets is limited to 1GB
13///
14/// There are many additional restrictions; see the [Alibaba Cloud documentation](https://help.aliyun.com/document_detail/31979.html) for details
15pub struct CopyObject {
16    req: OssRequest,
17    tags: HashMap<String, String>,
18}
19
20impl CopyObject {
21    pub(super) fn new(oss: Oss, copy_source: impl ToString) -> Self {
22        let mut req = OssRequest::new(oss, Method::PUT);
23        req.insert_header("x-oss-copy-source", copy_source);
24        CopyObject {
25            req,
26            tags: HashMap::new(),
27        }
28    }
29    /// Set the object's access permissions
30    pub fn set_acl(mut self, acl: Acl) -> Self {
31        self.req.insert_header("x-oss-object-acl", acl);
32        self
33    }
34    /// Set the object's storage class
35    pub fn set_storage_class(mut self, storage_class: StorageClass) -> Self {
36        self.req.insert_header("x-oss-storage-class", storage_class);
37        self
38    }
39    /// Set additional metadata
40    ///
41    /// Keys may only contain letters, numbers, and hyphens; metadata with other characters will be discarded
42    pub fn set_meta(mut self, key: impl ToString, value: impl ToString) -> Self {
43        let key = key.to_string();
44        if !invalid_metadata_key(&key) {
45            self.req
46                .insert_header(format!("x-oss-meta-{}", key.to_string()), value);
47        }
48        self
49    }
50    /// If the specified time is earlier than the file's actual modification time, the copy proceeds.
51    ///
52    pub fn set_if_modified_since(mut self, if_modified_since: OffsetDateTime) -> Self {
53        self.req.insert_header(
54            "x-oss-copy-source-if-modified-since",
55            format_gmt(if_modified_since),
56        );
57        self
58    }
59    /// If the specified time is equal to or later than the file's actual modification time, the copy proceeds.
60    ///
61    pub fn set_if_unmodified_since(mut self, if_unmodified_since: OffsetDateTime) -> Self {
62        self.req.insert_header(
63            "x-oss-copy-source-if-unmodified-since",
64            format_gmt(if_unmodified_since),
65        );
66        self
67    }
68    /// Copy the source object only if its ETag matches the value you provide.
69    ///
70    /// The ETag is used to verify whether the data has changed; you can use it to check data integrity.
71    pub fn set_if_match(mut self, if_match: impl ToString) -> Self {
72        self.req
73            .insert_header("x-oss-copy-source-if-match", if_match);
74        self
75    }
76    /// Copy the source object only if its ETag does not match the value you provide.
77    ///
78    /// The ETag is used to verify whether the data has changed; you can use it to check data integrity.
79    pub fn set_if_none_match(mut self, if_none_match: impl ToString) -> Self {
80        self.req
81            .insert_header("x-oss-copy-source-if-none-match", if_none_match);
82        self
83    }
84    /// Disallow overwriting files with the same name
85    pub fn forbid_overwrite(mut self) -> Self {
86        self.req.insert_header("x-oss-forbid-overwrite", "true");
87        self
88    }
89    /// Set tag information
90    pub fn set_tagging(mut self, key: impl ToString, value: impl ToString) -> Self {
91        self.tags.insert(key.to_string(), value.to_string());
92        self
93    }
94    /// Use the metadata specified in the request and ignore the source object's metadata
95    pub fn set_metadata_directive(mut self) -> Self {
96        self.req
97            .insert_header("x-oss-metadata-directive", "REPLACE");
98        self
99    }
100    /// Use the tags specified in the request and ignore the source object's tags
101    pub fn set_tagging_directive(mut self) -> Self {
102        self.req.insert_header("x-oss-tagging-directive", "Replace");
103        self
104    }
105
106    /// Copy the object
107    ///
108    pub async fn send(mut self) -> Result<(), Error> {
109        // Insert tags
110        let tags = self
111            .tags
112            .into_iter()
113            .map(|(key, value)| {
114                if value.is_empty() {
115                    url_encode(&key.to_string())
116                } else {
117                    format!(
118                        "{}={}",
119                        url_encode(&key.to_string()),
120                        url_encode(&value.to_string())
121                    )
122                }
123            })
124            .collect::<Vec<_>>()
125            .join("&");
126        if !tags.is_empty() {
127            self.req.insert_header("x-oss-tagging", tags);
128        }
129        // Build the HTTP request
130        let response = self.req.send_to_oss()?.await?;
131        // Parse the response
132        let status_code = response.status();
133        match status_code {
134            code if code.is_success() => Ok(()),
135            _ => Err(normal_error(response).await),
136        }
137    }
138}