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