1use bytes::Bytes;
2use http::{HeaderMap, StatusCode};
3
4use crate::error::Error;
5use crate::Result;
6
7#[derive(Clone)]
9pub struct Response {
10 status: StatusCode,
11 headers: HeaderMap,
12 body: Bytes,
13 url: Option<url::Url>,
14 #[cfg(feature = "json")]
15 json_parser: Option<crate::json_parser::JsonParserFn>,
16}
17
18impl Response {
19 pub(crate) fn new(
20 status: StatusCode,
21 headers: HeaderMap,
22 body: Bytes,
23 url: Option<url::Url>,
24 #[cfg(feature = "json")] json_parser: Option<crate::json_parser::JsonParserFn>,
25 ) -> Self {
26 Self {
27 status,
28 headers,
29 body,
30 url,
31 #[cfg(feature = "json")]
32 json_parser,
33 }
34 }
35
36 pub fn status(&self) -> StatusCode {
37 self.status
38 }
39
40 pub fn headers(&self) -> &HeaderMap {
41 &self.headers
42 }
43
44 pub fn bytes(&self) -> &Bytes {
45 &self.body
46 }
47
48 pub fn url(&self) -> Option<&url::Url> {
49 self.url.as_ref()
50 }
51
52 pub fn is_success(&self) -> bool {
53 self.status.is_success()
54 }
55
56 #[must_use = "call `?` or handle the error explicitly"]
58 pub fn error_for_status(&self) -> Result<()> {
59 if self.status.is_success() {
60 return Ok(());
61 }
62 Err(Error::http_with_status_text(
63 self.status,
64 self.status.canonical_reason().unwrap_or("request failed"),
65 self.status.canonical_reason().unwrap_or("request failed"),
66 Some(self.body.clone()),
67 ))
68 }
69
70 pub fn into_text(self) -> Result<String> {
72 self.error_for_status()?;
73 Ok(String::from_utf8_lossy(&self.body).into_owned())
74 }
75
76 pub async fn text(self) -> Result<String> {
78 self.into_text()
79 }
80
81 pub fn into_bytes_checked(self) -> Result<Bytes> {
83 self.error_for_status()?;
84 Ok(self.body)
85 }
86
87 pub async fn bytes_checked(self) -> Result<Bytes> {
89 self.into_bytes_checked()
90 }
91
92 #[cfg(feature = "json")]
93 pub fn into_json<T: serde::de::DeserializeOwned>(self) -> Result<T> {
94 self.error_for_status()?;
95 crate::json_parser::deserialize(&self.body, self.status, self.json_parser.as_ref())
96 }
97
98 #[cfg(feature = "json")]
99 pub async fn json<T: serde::de::DeserializeOwned>(self) -> Result<T> {
100 self.into_json()
101 }
102
103 #[cfg(feature = "json")]
104 pub fn into_json_unchecked<T: serde::de::DeserializeOwned>(self) -> Result<T> {
105 crate::json_parser::deserialize(&self.body, self.status, self.json_parser.as_ref())
106 }
107
108 #[cfg(feature = "json")]
109 pub async fn json_unchecked<T: serde::de::DeserializeOwned>(self) -> Result<T> {
110 self.into_json_unchecked()
111 }
112
113 #[cfg(feature = "validate")]
115 pub fn into_json_validated<T>(self) -> Result<T>
116 where
117 T: serde::de::DeserializeOwned + garde::Validate,
118 T::Context: Default,
119 {
120 self.error_for_status()?;
121 crate::validate_json::deserialize_validated(
122 &self.body,
123 self.status,
124 self.json_parser.as_ref(),
125 )
126 }
127
128 #[cfg(feature = "validate")]
130 pub async fn json_validated<T>(self) -> Result<T>
131 where
132 T: serde::de::DeserializeOwned + garde::Validate,
133 T::Context: Default,
134 {
135 self.into_json_validated()
136 }
137
138 #[cfg(feature = "validate")]
140 pub fn into_json_validated_unchecked<T>(self) -> Result<T>
141 where
142 T: serde::de::DeserializeOwned + garde::Validate,
143 T::Context: Default,
144 {
145 crate::validate_json::deserialize_validated(
146 &self.body,
147 self.status,
148 self.json_parser.as_ref(),
149 )
150 }
151
152 #[cfg(feature = "validate")]
154 pub async fn json_validated_unchecked<T>(self) -> Result<T>
155 where
156 T: serde::de::DeserializeOwned + garde::Validate,
157 T::Context: Default,
158 {
159 self.into_json_validated_unchecked()
160 }
161
162 pub fn into_parts(self) -> (StatusCode, HeaderMap, Bytes) {
163 (self.status, self.headers, self.body)
164 }
165}
166
167impl std::fmt::Debug for Response {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 let mut debug = f.debug_struct("Response");
170 debug
171 .field("status", &self.status)
172 .field("headers", &self.headers)
173 .field("body", &self.body)
174 .field("url", &self.url);
175 #[cfg(feature = "json")]
176 if self.json_parser.is_some() {
177 debug.field("json_parser", &"<custom>");
178 }
179 debug.finish()
180 }
181}
182
183#[cfg(all(test, feature = "json"))]
184mod tests {
185 use super::*;
186 use serde::Deserialize;
187
188 #[derive(Debug, Deserialize, PartialEq)]
189 struct IdOnly {
190 id: u64,
191 }
192
193 #[test]
194 fn into_text_returns_body_on_success() {
195 let response = Response::new(
196 StatusCode::OK,
197 HeaderMap::new(),
198 Bytes::from_static(b"hello"),
199 None,
200 None,
201 );
202 assert_eq!(response.into_text().unwrap(), "hello");
203 }
204
205 #[test]
206 fn into_json_deserializes_without_async() {
207 let response = Response::new(
208 StatusCode::OK,
209 HeaderMap::new(),
210 Bytes::from_static(br#"{"id":7}"#),
211 None,
212 None,
213 );
214 assert_eq!(response.into_json::<IdOnly>().unwrap(), IdOnly { id: 7 });
215 }
216}