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}