Skip to main content

tame_gcs/v1/objects/
rewrite.rs

1use crate::{
2    common::{Conditionals, Projection, StandardQueryParameters},
3    error::Error,
4    response::ApiResponse,
5    types::ObjectIdentifier,
6};
7
8#[derive(Default, Serialize)]
9#[serde(rename_all = "camelCase")]
10pub struct RewriteObjectOptional<'a> {
11    #[serde(flatten)]
12    pub standard_params: StandardQueryParameters<'a>,
13    /// Resource name of the Cloud KMS key that will be used to encrypt the
14    /// object. The Cloud KMS key must be located in same location as the object.
15    ///
16    /// If the parameter is not specified, the method uses the destination
17    /// bucket's default encryption key, if any, or the Google-managed encryption
18    /// key.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub destination_kms_key_name: Option<String>,
21    /// Apply a predefined set of access controls to the destination object.
22    ///
23    /// Acceptable values are:
24    ///
25    /// * authenticatedRead: Object owner gets OWNER access, and
26    ///   allAuthenticatedUsers get READER access.
27    /// * bucketOwnerFullControl: Object owner gets OWNER access, and project
28    ///   team owners get OWNER access.
29    /// * bucketOwnerRead: Object owner gets OWNER access, and project team
30    ///   owners get READER access.
31    /// * private: Object owner gets OWNER access.
32    /// * projectPrivate: Object owner gets OWNER access, and project team
33    ///   members get access according to their roles.
34    /// * publicRead: Object owner gets OWNER access, and allUsers get READER access.
35    ///
36    /// If iamConfiguration.uniformBucketLevelAccess.enabled is set to true,
37    /// requests that include this parameter fail with a 400 Bad Request response.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub destination_predefined_acl: Option<String>,
40    #[serde(flatten, skip_serializing_if = "Option::is_none")]
41    pub destination_conditionals: Option<Conditionals>,
42    /// Makes the operation conditional on whether the source object's
43    /// generation matches the given value.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub if_source_generation_match: Option<i64>,
46    /// Makes the operation conditional on whether the source object's
47    /// generation does not match the given value.
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub if_source_generation_not_match: Option<i64>,
50    /// Makes the operation conditional on whether the source object's current
51    /// metageneration matches the given value.
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub if_source_metageneration_match: Option<i64>,
54    /// Makes the operation conditional on whether the source object's current
55    /// metageneration does not match the given value.
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub if_source_metageneration_not_match: Option<i64>,
58    /// The maximum number of bytes that will be rewritten per rewrite request.
59    /// Most callers shouldn't need to specify this parameter - it is primarily
60    /// in place to support testing. If specified the value must be an integral
61    /// multiple of 1 MiB (1048576). Also, this only applies to requests where
62    /// the source and destination span locations and/or storage classes.
63    /// Finally, this value must not change across rewrite calls else you'll get
64    /// an error that the rewriteToken is invalid.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub max_bytes_rewritten_per_call: Option<i64>,
67    /// Set of properties to return. Defaults to `noAcl`, unless the object
68    /// resource specifies the acl property, when it defaults to full.
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub projection: Option<Projection>,
71    /// If present, selects a specific revision of the source object (as opposed
72    /// to the latest version, the default).
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub source_generation: Option<i64>,
75}
76
77#[derive(Deserialize)]
78#[serde(rename_all = "camelCase")]
79pub struct RewriteObjectResponse {
80    /// The number of bytes that have been rewritten thusfar
81    #[serde(deserialize_with = "crate::objects::from_str")]
82    pub total_bytes_rewritten: u64,
83    /// The total size of the original source object
84    #[serde(deserialize_with = "crate::objects::from_str")]
85    pub object_size: u64,
86    /// Indicates if the rewrite is finished or not
87    pub done: bool,
88    /// If done is false, this will be `Some` and it must be specified in each
89    /// additional rewrite call until done is true
90    pub rewrite_token: Option<String>,
91    #[serde(rename = "resource")]
92    pub metadata: Option<super::Metadata>,
93}
94
95impl ApiResponse<bytes::Bytes> for RewriteObjectResponse {}
96
97impl<B> TryFrom<http::Response<B>> for RewriteObjectResponse
98where
99    B: AsRef<[u8]>,
100{
101    type Error = Error;
102
103    fn try_from(response: http::Response<B>) -> Result<Self, Self::Error> {
104        let (_parts, body) = response.into_parts();
105        Ok(serde_json::from_slice(body.as_ref())?)
106    }
107}
108
109impl super::Object {
110    /// Rewrites a source object to a destination object. Optionally overrides metadata.
111    ///
112    /// Required IAM Permissions:
113    /// * `storage.objects.create` (for the destination bucket)
114    /// * `storage.objects.delete` (for the destination bucket)
115    /// * `storage.objects.get` (for the source bucket)
116    ///
117    /// [Complete API Documentation](https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite)
118    pub fn rewrite<'a, OID>(
119        &self,
120        source: &OID,
121        destination: &OID,
122        rewrite_token: Option<String>,
123        metadata: Option<&super::Metadata>,
124        optional: Option<RewriteObjectOptional<'_>>,
125    ) -> Result<http::Request<std::io::Cursor<Vec<u8>>>, Error>
126    where
127        OID: ObjectIdentifier<'a> + ?Sized,
128    {
129        let mut uri = format!(
130            "https://{}/storage/v1/b/{}/o/{}/rewriteTo/b/{}/o/{}",
131            self.authority.as_str(),
132            percent_encoding::percent_encode(
133                source.bucket().as_ref(),
134                crate::util::PATH_ENCODE_SET
135            ),
136            percent_encoding::percent_encode(
137                source.object().as_ref(),
138                crate::util::PATH_ENCODE_SET
139            ),
140            percent_encoding::percent_encode(
141                destination.bucket().as_ref(),
142                crate::util::PATH_ENCODE_SET
143            ),
144            percent_encoding::percent_encode(
145                destination.object().as_ref(),
146                crate::util::PATH_ENCODE_SET
147            )
148        );
149
150        let query = optional.unwrap_or_default();
151        let query_params = serde_urlencoded::to_string(query)?;
152        if !query_params.is_empty() || rewrite_token.is_some() {
153            uri.push('?');
154
155            if let Some(rt) = rewrite_token {
156                uri.push_str("rewriteToken=");
157                uri.push_str(&rt);
158
159                if !query_params.is_empty() {
160                    uri.push('&');
161                }
162            }
163
164            if !query_params.is_empty() {
165                uri.push_str(&query_params);
166            }
167        }
168
169        let mut req_builder = http::Request::builder();
170
171        let body = match metadata {
172            Some(metadata) => {
173                let md = serde_json::to_vec(&metadata)?;
174                let len = md.len();
175
176                req_builder = req_builder
177                    .header("content-type", "application/json")
178                    .header("content-length", len);
179
180                std::io::Cursor::new(md)
181            }
182            None => std::io::Cursor::new(Vec::new()),
183        };
184
185        Ok(req_builder.method("POST").uri(uri).body(body)?)
186    }
187}