1use std::collections::HashMap;
14
15use bytes::Bytes;
16
17#[cfg(feature = "streaming")]
23pub mod streaming {
24 use std::collections::HashMap;
25 use std::pin::Pin;
26
27 use bytes::Bytes;
28 use futures_core::Stream;
29 use futures_util::StreamExt;
30
31 pub type StreamingBody = Pin<Box<dyn Stream<Item = crate::Result<Bytes>> + Send>>;
33
34 pub struct StreamingResponse {
38 status: u16,
39 headers: HashMap<String, String>,
40 body: StreamingBody,
41 }
42
43 impl StreamingResponse {
44 #[must_use]
46 pub fn new(status: u16, headers: HashMap<String, String>, body: StreamingBody) -> Self {
47 Self {
48 status,
49 headers,
50 body,
51 }
52 }
53
54 #[must_use]
56 pub const fn status(&self) -> u16 {
57 self.status
58 }
59
60 #[must_use]
62 pub fn headers(&self) -> &HashMap<String, String> {
63 &self.headers
64 }
65
66 #[must_use]
68 pub fn header(&self, name: &str) -> Option<&str> {
69 self.headers.get(name).map(String::as_str)
70 }
71
72 #[must_use]
74 pub const fn is_success(&self) -> bool {
75 self.status >= 200 && self.status < 300
76 }
77
78 #[must_use]
80 pub const fn is_client_error(&self) -> bool {
81 self.status >= 400 && self.status < 500
82 }
83
84 #[must_use]
86 pub const fn is_server_error(&self) -> bool {
87 self.status >= 500 && self.status < 600
88 }
89
90 #[must_use]
92 pub fn into_body(self) -> StreamingBody {
93 self.body
94 }
95
96 pub async fn collect(self) -> crate::Result<super::Response<Bytes>> {
102 let mut body = self.body;
103 let mut collected = Vec::new();
104
105 while let Some(chunk) = body.next().await {
106 collected.extend_from_slice(&chunk?);
107 }
108
109 Ok(super::Response::new(
110 self.status,
111 self.headers,
112 Bytes::from(collected),
113 ))
114 }
115 }
116}
117
118#[derive(Debug, Clone)]
124pub struct Response<B = Bytes> {
125 status: u16,
126 headers: HashMap<String, String>,
127 body: B,
128}
129
130impl<B> Response<B> {
131 #[must_use]
133 pub fn new(status: u16, headers: HashMap<String, String>, body: B) -> Self {
134 Self {
135 status,
136 headers,
137 body,
138 }
139 }
140
141 #[must_use]
143 pub const fn status(&self) -> u16 {
144 self.status
145 }
146
147 #[must_use]
149 pub fn headers(&self) -> &HashMap<String, String> {
150 &self.headers
151 }
152
153 #[must_use]
155 pub fn header(&self, name: &str) -> Option<&str> {
156 self.headers.get(name).map(String::as_str)
157 }
158
159 #[must_use]
161 pub const fn body(&self) -> &B {
162 &self.body
163 }
164
165 #[must_use]
167 pub fn into_body(self) -> B {
168 self.body
169 }
170
171 #[must_use]
173 pub fn into_parts(self) -> (u16, HashMap<String, String>, B) {
174 (self.status, self.headers, self.body)
175 }
176
177 #[must_use]
179 pub const fn is_success(&self) -> bool {
180 self.status >= 200 && self.status < 300
181 }
182
183 #[must_use]
185 pub const fn is_redirection(&self) -> bool {
186 self.status >= 300 && self.status < 400
187 }
188
189 #[must_use]
191 pub const fn is_client_error(&self) -> bool {
192 self.status >= 400 && self.status < 500
193 }
194
195 #[must_use]
197 pub const fn is_server_error(&self) -> bool {
198 self.status >= 500 && self.status < 600
199 }
200
201 pub fn map_body<F, B2>(self, f: F) -> Response<B2>
203 where
204 F: FnOnce(B) -> B2,
205 {
206 Response {
207 status: self.status,
208 headers: self.headers,
209 body: f(self.body),
210 }
211 }
212}
213
214impl Response<Bytes> {
215 pub fn json<T: serde::de::DeserializeOwned>(self) -> crate::Result<T> {
221 crate::from_json(&self.body)
222 }
223
224 pub fn text(self) -> Result<String, std::string::FromUtf8Error> {
230 String::from_utf8(self.body.to_vec())
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn response_basic() {
240 let mut headers = HashMap::new();
241 headers.insert("Content-Type".to_string(), "application/json".to_string());
242
243 let response = Response::new(200, headers, Bytes::from(r#"{"id":1}"#));
244
245 assert_eq!(response.status(), 200);
246 assert_eq!(response.header("Content-Type"), Some("application/json"));
247 assert!(response.is_success());
248 assert!(!response.is_client_error());
249 assert!(!response.is_server_error());
250 }
251
252 #[test]
253 fn response_status_checks() {
254 let response = Response::new(301, HashMap::new(), Bytes::new());
255 assert!(response.is_redirection());
256
257 let response = Response::new(404, HashMap::new(), Bytes::new());
258 assert!(response.is_client_error());
259
260 let response = Response::new(500, HashMap::new(), Bytes::new());
261 assert!(response.is_server_error());
262 }
263
264 #[test]
265 fn response_json() {
266 #[derive(Debug, PartialEq, serde::Deserialize)]
267 struct User {
268 id: u64,
269 name: String,
270 }
271
272 let body = Bytes::from(r#"{"id":1,"name":"test"}"#);
273 let response = Response::new(200, HashMap::new(), body);
274
275 let user: User = response.json().expect("deserialize");
276 assert_eq!(
277 user,
278 User {
279 id: 1,
280 name: "test".to_string()
281 }
282 );
283 }
284
285 #[test]
286 fn response_text() {
287 let body = Bytes::from("Hello, World!");
288 let response = Response::new(200, HashMap::new(), body);
289
290 let text = response.text().expect("text");
291 assert_eq!(text, "Hello, World!");
292 }
293
294 #[test]
295 fn response_map_body() {
296 let response = Response::new(200, HashMap::new(), Bytes::from("test"));
297 let mapped = response.map_body(|b| b.len());
298
299 assert_eq!(mapped.status(), 200);
300 assert_eq!(*mapped.body(), 4);
301 }
302}