1use bytes::Bytes;
7use http::{HeaderMap, StatusCode};
8
9use crate::error::Error;
10use crate::Result;
11
12#[derive(Clone)]
23pub struct Response {
24 status: StatusCode,
25 headers: HeaderMap,
26 body: Bytes,
27 url: Option<url::Url>,
28 #[cfg(feature = "json")]
29 json_parser: Option<crate::json_parser::JsonParserFn>,
30}
31
32impl Response {
33 pub(crate) fn new(
34 status: StatusCode,
35 headers: HeaderMap,
36 body: Bytes,
37 url: Option<url::Url>,
38 #[cfg(feature = "json")] json_parser: Option<crate::json_parser::JsonParserFn>,
39 ) -> Self {
40 Self {
41 status,
42 headers,
43 body,
44 url,
45 #[cfg(feature = "json")]
46 json_parser,
47 }
48 }
49
50 pub fn status(&self) -> StatusCode {
52 self.status
53 }
54
55 pub fn headers(&self) -> &HeaderMap {
57 &self.headers
58 }
59
60 pub fn bytes(&self) -> &Bytes {
62 &self.body
63 }
64
65 pub fn url(&self) -> Option<&url::Url> {
67 self.url.as_ref()
68 }
69
70 pub fn is_success(&self) -> bool {
72 self.status.is_success()
73 }
74
75 #[must_use = "call `?` or handle the error explicitly"]
77 pub fn error_for_status(&self) -> Result<()> {
78 if self.status.is_success() {
79 return Ok(());
80 }
81 Err(Error::http_with_status_text(
82 self.status,
83 self.status.canonical_reason().unwrap_or("request failed"),
84 self.status.canonical_reason().unwrap_or("request failed"),
85 Some(self.body.clone()),
86 ))
87 }
88
89 pub fn into_text(self) -> Result<String> {
93 self.error_for_status()?;
94 Ok(String::from_utf8_lossy(&self.body).into_owned())
95 }
96
97 pub async fn text(self) -> Result<String> {
99 self.into_text()
100 }
101
102 pub fn into_bytes_checked(self) -> Result<Bytes> {
106 self.error_for_status()?;
107 Ok(self.body)
108 }
109
110 pub async fn bytes_checked(self) -> Result<Bytes> {
112 self.into_bytes_checked()
113 }
114
115 #[cfg(feature = "json")]
121 pub fn into_json<T: serde::de::DeserializeOwned>(self) -> Result<T> {
122 self.error_for_status()?;
123 crate::json_parser::deserialize(&self.body, self.status, self.json_parser.as_ref())
124 }
125
126 #[cfg(feature = "json")]
128 pub async fn json<T: serde::de::DeserializeOwned>(self) -> Result<T> {
129 self.into_json()
130 }
131
132 #[cfg(feature = "json")]
138 pub fn into_json_with<T, F>(self, parse: F) -> Result<T>
139 where
140 T: serde::de::DeserializeOwned,
141 F: FnOnce(&Bytes) -> std::result::Result<T, String>,
142 {
143 self.error_for_status()?;
144 parse(&self.body).map_err(|message| {
145 crate::json_parser::deserialize_error(self.status, message, &self.body)
146 })
147 }
148
149 #[cfg(feature = "json")]
151 pub async fn json_with<T, F>(self, parse: F) -> Result<T>
152 where
153 T: serde::de::DeserializeOwned,
154 F: FnOnce(&Bytes) -> std::result::Result<T, String>,
155 {
156 self.into_json_with(parse)
157 }
158
159 #[cfg(feature = "json")]
161 pub fn into_json_unchecked<T: serde::de::DeserializeOwned>(self) -> Result<T> {
162 crate::json_parser::deserialize(&self.body, self.status, self.json_parser.as_ref())
163 }
164
165 #[cfg(feature = "json")]
167 pub async fn json_unchecked<T: serde::de::DeserializeOwned>(self) -> Result<T> {
168 self.into_json_unchecked()
169 }
170
171 #[cfg(feature = "validate")]
173 pub fn into_json_validated<T>(self) -> Result<T>
174 where
175 T: serde::de::DeserializeOwned + garde::Validate,
176 T::Context: Default,
177 {
178 self.error_for_status()?;
179 crate::validate_json::deserialize_validated(
180 &self.body,
181 self.status,
182 self.json_parser.as_ref(),
183 )
184 }
185
186 #[cfg(feature = "validate")]
188 pub async fn json_validated<T>(self) -> Result<T>
189 where
190 T: serde::de::DeserializeOwned + garde::Validate,
191 T::Context: Default,
192 {
193 self.into_json_validated()
194 }
195
196 #[cfg(feature = "validate")]
198 pub fn into_json_validated_unchecked<T>(self) -> Result<T>
199 where
200 T: serde::de::DeserializeOwned + garde::Validate,
201 T::Context: Default,
202 {
203 crate::validate_json::deserialize_validated(
204 &self.body,
205 self.status,
206 self.json_parser.as_ref(),
207 )
208 }
209
210 #[cfg(feature = "validate")]
212 pub async fn json_validated_unchecked<T>(self) -> Result<T>
213 where
214 T: serde::de::DeserializeOwned + garde::Validate,
215 T::Context: Default,
216 {
217 self.into_json_validated_unchecked()
218 }
219
220 pub fn into_parts(self) -> (StatusCode, HeaderMap, Bytes) {
222 (self.status, self.headers, self.body)
223 }
224}
225
226impl std::fmt::Debug for Response {
227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228 let mut debug = f.debug_struct("Response");
229 debug
230 .field("status", &self.status)
231 .field("headers", &self.headers)
232 .field("body", &self.body)
233 .field("url", &self.url);
234 #[cfg(feature = "json")]
235 if self.json_parser.is_some() {
236 debug.field("json_parser", &"<custom>");
237 }
238 debug.finish()
239 }
240}
241
242#[cfg(all(test, feature = "json"))]
243mod tests {
244 use super::*;
245 use serde::Deserialize;
246
247 #[derive(Debug, Deserialize, PartialEq)]
248 struct IdOnly {
249 id: u64,
250 }
251
252 #[test]
253 fn into_text_returns_body_on_success() {
254 let response = Response::new(
255 StatusCode::OK,
256 HeaderMap::new(),
257 Bytes::from_static(b"hello"),
258 None,
259 None,
260 );
261 assert_eq!(response.into_text().unwrap(), "hello");
262 }
263
264 #[test]
265 fn into_json_deserializes_without_async() {
266 let response = Response::new(
267 StatusCode::OK,
268 HeaderMap::new(),
269 Bytes::from_static(br#"{"id":7}"#),
270 None,
271 None,
272 );
273 assert_eq!(response.into_json::<IdOnly>().unwrap(), IdOnly { id: 7 });
274 }
275
276 #[test]
277 fn into_json_with_strips_bom_without_client_parser() {
278 let response = Response::new(
279 StatusCode::OK,
280 HeaderMap::new(),
281 Bytes::from_static(b"\xef\xbb\xbf{\"id\":3}"),
282 None,
283 None,
284 );
285 let parsed: IdOnly = response
286 .into_json_with(|body| {
287 let slice = body.strip_prefix(b"\xef\xbb\xbf").unwrap_or(body);
288 serde_json::from_slice(slice).map_err(|e| e.to_string())
289 })
290 .unwrap();
291 assert_eq!(parsed, IdOnly { id: 3 });
292 }
293}