google_cloud_storage/
builder_ext.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Extends [builder][crate::builder] with types that improve type safety and/or
16//! ergonomics.
17
18/// An extension trait for `RewriteObject` to provide a convenient way
19/// to poll a rewrite operation until it is complete.
20#[async_trait::async_trait]
21pub trait RewriteObjectExt {
22    /// Sends the request and polls the operation until it is complete.
23    ///
24    /// This helper function simplifies the process of handling a
25    /// [StorageControl::rewrite_object][crate::client::StorageControl::rewrite_object]
26    /// operation, which may require multiple requests to complete. It automatically
27    /// handles the logic of sending the
28    /// [rewrite_token][crate::generated::gapic::model::RewriteObjectRequest::rewrite_token]
29    /// from one response in the next request.
30    ///
31    /// For more details on this loop, see the "Rewriting objects" section of the
32    /// user guide:
33    /// <https://googleapis.github.io/google-cloud-rust/storage/rewrite_object.html>
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// # use google_cloud_storage::client::StorageControl;
39    /// # use google_cloud_storage::builder_ext::RewriteObjectExt;
40    /// # async fn sample(client: &StorageControl) -> anyhow::Result<()> {
41    /// const SOURCE_NAME: &str = "object-to-copy";
42    /// const DEST_NAME: &str = "copied-object";
43    /// let source_bucket_id = "source-bucket";
44    /// let dest_bucket_id = "dest-bucket";
45    /// let copied = client
46    ///     .rewrite_object()
47    ///     .set_source_bucket(format!("projects/_/buckets/{source_bucket_id}"))
48    ///     .set_source_object(SOURCE_NAME)
49    ///     .set_destination_bucket(format!("projects/_/buckets/{dest_bucket_id}"))
50    ///     .set_destination_name(DEST_NAME)
51    ///     .rewrite_until_done()
52    ///     .await?;
53    /// # Ok(())
54    /// # }
55    /// ```
56    async fn rewrite_until_done(self) -> crate::Result<crate::model::Object>;
57}
58
59#[async_trait::async_trait]
60impl RewriteObjectExt for crate::builder::storage_control::RewriteObject {
61    async fn rewrite_until_done(mut self) -> crate::Result<crate::model::Object> {
62        loop {
63            let resp = self.clone().send().await?;
64            if resp.done {
65                return Ok(resp
66                    .resource
67                    .expect("an object is always returned when the rewrite operation is done"));
68            }
69            self = self.set_rewrite_token(resp.rewrite_token);
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::client::StorageControl;
78    use crate::model::{Object, RewriteObjectRequest, RewriteResponse};
79    use gax::error::rpc::{Code, Status};
80    use gax::options::RequestOptions;
81    use gax::response::Response;
82
83    mockall::mock! {
84        #[derive(Debug)]
85        StorageControl {}
86        impl crate::stub::StorageControl for StorageControl {
87            async fn rewrite_object( &self, _req: RewriteObjectRequest, _options: RequestOptions) -> gax::Result<Response<RewriteResponse>>;
88        }
89    }
90
91    #[tokio::test]
92    async fn test_rewrite_until_done() -> anyhow::Result<()> {
93        let mut mock = MockStorageControl::new();
94        let final_object = Object::new().set_name("final-object");
95
96        let mut seq = mockall::Sequence::new();
97        mock.expect_rewrite_object()
98            .withf(|req: &RewriteObjectRequest, _| req.rewrite_token.is_empty())
99            .times(1)
100            .in_sequence(&mut seq)
101            .returning(|_, _| {
102                Ok(Response::from(
103                    RewriteResponse::new()
104                        .set_done(false)
105                        .set_rewrite_token("token1"),
106                ))
107            });
108
109        mock.expect_rewrite_object()
110            .withf(|req: &RewriteObjectRequest, _| req.rewrite_token == "token1")
111            .times(1)
112            .in_sequence(&mut seq)
113            .returning({
114                let obj = final_object.clone();
115                move |_, _| {
116                    Ok(Response::from(
117                        RewriteResponse::new()
118                            .set_done(true)
119                            .set_resource(obj.clone()),
120                    ))
121                }
122            });
123
124        let client = StorageControl::from_stub(mock);
125        let result = client.rewrite_object().rewrite_until_done().await?;
126
127        assert_eq!(result, final_object);
128        Ok(())
129    }
130
131    #[tokio::test]
132    async fn test_rewrite_until_done_error() -> anyhow::Result<()> {
133        let mut mock = MockStorageControl::new();
134        mock.expect_rewrite_object()
135            .withf(|req: &RewriteObjectRequest, _| req.rewrite_token.is_empty())
136            .times(1)
137            .returning(|_, _| {
138                Err(gax::error::Error::service(
139                    Status::default().set_code(Code::Unavailable),
140                ))
141            });
142
143        let client = StorageControl::from_stub(mock);
144        let _ = client
145            .rewrite_object()
146            .rewrite_until_done()
147            .await
148            .unwrap_err();
149
150        Ok(())
151    }
152}