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