Skip to main content

google_cloud_gax/
response.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//! Response types.
16//!
17//! This module contains types related to Google Cloud service responses.
18//! Notably it contains the `Response` type itself. Typically you'll import
19//! this type.
20//!
21//! # Examples
22//!
23//! Inspecting the result of a request
24//!
25//! ```no_run
26//! # use google_cloud_gax::Result;
27//! # use google_cloud_gax::response::Response;
28//! // A type representing a Google Cloud service resource, for example, a
29//! // secret manager secret.
30//! struct Resource {
31//!   // ...
32//! }
33//!
34//! async fn make_google_service_rpc(project_id: &str) -> Result<Response<Resource>> {
35//!   // ...
36//! # panic!()
37//! }
38//!
39//! # async fn sample() -> anyhow::Result<()> {
40//! let response = make_google_service_rpc("my-project").await?;
41//! if let Some(date) = response.headers().get("Date") {
42//!     // do something with the date
43//! }
44//! let resource = response.body();
45//! // do something with
46//! # Ok(()) }
47//! ```
48//!
49//! Creating a response for mocks
50//!
51//! ```
52//! # use google_cloud_gax::Result;
53//! # use google_cloud_gax::response::Response;
54//! // A type representing a Google Cloud service resource, for example, a
55//! // secret manager secret.
56//! struct Resource {
57//!   // ...
58//! }
59//!
60//! fn make_mock_response(body: Resource) -> Result<Response<Resource>> {
61//!     Ok(Response::from(body))
62//! }
63//! ```
64
65/// Represents a Google Cloud service response.
66///
67/// A response from a Google Cloud service consists of a body (potentially the
68/// unit type), and some metadata, currently just headers.
69///
70/// Typically you get a response as the result of making a request via some
71/// client in the Google Cloud client libraries for Rust. You may also create
72/// responses directly when mocking clients for your own tests.
73///
74/// # Examples
75/// Inspecting the result of a request
76///
77/// ```no_run
78/// # use google_cloud_gax::Result;
79/// # use google_cloud_gax::response::Response;
80/// // A type representing a Google Cloud service resource, for example, a
81/// // secret manager secret.
82/// struct Resource {
83///   // ...
84/// }
85///
86/// async fn make_google_service_rpc(project_id: &str) -> Result<Response<Resource>> {
87///   // ...
88/// # panic!()
89/// }
90///
91/// # async fn sample() -> anyhow::Result<()> {
92/// let response = make_google_service_rpc("my-project").await?;
93/// if let Some(date) = response.headers().get("Date") {
94///     // do something with the date
95/// }
96/// let resource = response.body();
97/// // do something with
98/// # Ok(()) }
99/// ```
100///
101/// Creating a response for mocks
102///
103/// ```
104/// # use google_cloud_gax::Result;
105/// # use google_cloud_gax::response::Response;
106/// // A type representing a Google Cloud service resource, for example, a
107/// // secret manager secret.
108/// struct Resource {
109///   // ...
110/// }
111///
112/// fn make_mock_response(body: Resource) -> Result<Response<Resource>> {
113///     Ok(Response::from(body))
114/// }
115/// ```
116///
117#[derive(Clone, Debug)]
118pub struct Response<T> {
119    parts: Parts,
120    body: T,
121}
122
123impl<T> Response<T> {
124    /// Creates a response from the body.
125    ///
126    /// # Example
127    /// ```
128    /// # use google_cloud_gax::response::Response;
129    /// #[derive(Clone, Default)]
130    /// pub struct Resource {
131    ///   // ...
132    /// }
133    ///
134    /// let body = Resource::default();
135    /// let response = Response::from(body);
136    /// ```
137    pub fn from(body: T) -> Self {
138        Self {
139            body,
140            parts: Parts::default(),
141        }
142    }
143
144    /// Creates a response from the given parts.
145    ///
146    /// # Example
147    /// ```
148    /// # use google_cloud_gax::response::Response;
149    /// # use google_cloud_gax::response::Parts;
150    /// #[derive(Clone, Default)]
151    /// pub struct Resource {
152    ///   // ...
153    /// }
154    ///
155    /// let mut headers = http::HeaderMap::new();
156    /// headers.insert(http::header::CONTENT_TYPE, http::HeaderValue::from_static("application/json"));
157    /// let body = Resource::default();
158    /// let response : Response<Resource> = Response::from_parts(
159    ///     Parts::new().set_headers(headers), body);
160    /// assert!(response.headers().get(http::header::CONTENT_TYPE).is_some());
161    /// ```
162    pub fn from_parts(parts: Parts, body: T) -> Self {
163        Self { parts, body }
164    }
165
166    /// Returns the headers associated with this response.
167    ///
168    /// # Example
169    /// ```
170    /// # use google_cloud_gax::response::Response;
171    /// let response = Response::from(());
172    /// assert!(response.headers().is_empty());
173    /// ```
174    pub fn headers(&self) -> &http::HeaderMap<http::HeaderValue> {
175        &self.parts.headers
176    }
177
178    /// Returns the body associated with this response.
179    ///
180    /// # Example
181    /// ```
182    /// # use google_cloud_gax::response::Response;
183    /// let response = Response::from("test".to_string());
184    /// assert_eq!(response.body().as_str(), "test");
185    /// ```
186    pub fn body(&self) -> &T {
187        &self.body
188    }
189
190    /// Consumes the response returning the metadata, and body.
191    ///
192    /// # Example
193    /// ```
194    /// # use google_cloud_gax::response::Response;
195    /// let response = Response::from("test".to_string());
196    /// let (parts, body) = response.into_parts();
197    /// assert_eq!(body.as_str(), "test");
198    /// assert!(parts.headers.is_empty());
199    /// ```
200    pub fn into_parts(self) -> (Parts, T) {
201        (self.parts, self.body)
202    }
203
204    /// Consumes the response returning only its body.
205    ///
206    /// # Example
207    /// ```
208    /// # use google_cloud_gax::response::Response;
209    /// let response = Response::from("test".to_string());
210    /// let body = response.into_body();
211    /// assert_eq!(body.as_str(), "test");
212    /// ```
213    pub fn into_body(self) -> T {
214        self.body
215    }
216}
217
218/// Component parts of a response.
219///
220/// The response parts, other than the body, consist of just headers. We
221/// anticipate the addition of new fields over time.
222///
223/// The headers are used to return gRPC metadata, as well as (unsurprisingly)
224/// HTTP headers.
225///
226/// # Example
227/// ```
228/// # use google_cloud_gax::response::Parts;
229/// let mut headers = http::HeaderMap::new();
230/// headers.insert(http::header::CONTENT_TYPE, http::HeaderValue::from_static("application/json"));
231/// let parts = Parts::new().set_headers(headers);
232///
233/// assert_eq!(
234///     parts.headers.get(http::header::CONTENT_TYPE),
235///     Some(&http::HeaderValue::from_static("application/json"))
236/// );
237/// ```
238///
239/// [tower]: https://github.com/tower-rs/tower
240#[derive(Clone, Debug, Default)]
241#[non_exhaustive]
242pub struct Parts {
243    /// The HTTP headers or the gRPC metadata converted to HTTP headers.
244    pub headers: http::HeaderMap<http::HeaderValue>,
245}
246
247impl Parts {
248    /// Create a new instance.
249    ///
250    /// # Example
251    /// ```
252    /// # use google_cloud_gax::response::Parts;
253    /// let parts = Parts::new();
254    /// assert!(parts.headers.is_empty());
255    /// ```
256    pub fn new() -> Self {
257        Parts::default()
258    }
259
260    /// Set the headers.
261    ///
262    /// # Example
263    /// ```
264    /// # use google_cloud_gax::response::Parts;
265    /// let mut headers = http::HeaderMap::new();
266    /// headers.insert(
267    ///     http::header::CONTENT_TYPE,
268    ///     http::HeaderValue::from_static("application/json"),
269    /// );
270    /// let parts = Parts::new().set_headers(headers.clone());
271    /// assert_eq!(parts.headers, headers);
272    /// ```
273    pub fn set_headers<V>(mut self, v: V) -> Self
274    where
275        V: Into<http::HeaderMap>,
276    {
277        self.headers = v.into();
278        self
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    use super::*;
285
286    #[test]
287    fn response_from() {
288        let response = Response::from("abc123".to_string());
289        assert!(response.headers().is_empty(), "{:?}", response.headers());
290        assert_eq!(response.body().as_str(), "abc123");
291
292        let body = response.into_body();
293        assert_eq!(body.as_str(), "abc123");
294    }
295
296    #[test]
297    fn response_from_parts() {
298        let mut headers = http::HeaderMap::new();
299        headers.insert(
300            http::header::CONTENT_TYPE,
301            http::HeaderValue::from_static("application/json"),
302        );
303        let parts = Parts::new().set_headers(headers.clone());
304
305        let response = Response::from_parts(parts, "abc123".to_string());
306        assert_eq!(response.body().as_str(), "abc123");
307        assert_eq!(response.headers(), &headers);
308
309        let (parts, body) = response.into_parts();
310        assert_eq!(body.as_str(), "abc123");
311        assert_eq!(parts.headers, headers);
312    }
313
314    #[test]
315    fn parts() {
316        let parts = Parts::new();
317        assert!(
318            parts.headers.is_empty(),
319            "Parts::new().headers should be empty, got={:?}",
320            parts.headers
321        );
322
323        let mut headers = http::HeaderMap::new();
324        headers.insert(
325            http::header::CONTENT_TYPE,
326            http::HeaderValue::from_static("application/json"),
327        );
328        let parts = Parts::new().set_headers(headers.clone());
329
330        assert_eq!(parts.headers, headers);
331        assert_eq!(
332            parts.headers.get(http::header::CONTENT_TYPE),
333            Some(&http::HeaderValue::from_static("application/json"))
334        );
335    }
336}