1mod headers;
2
3use std::{borrow::Cow, convert::Infallible, future::ready};
4
5use bytes::{Bytes, BytesMut};
6pub use headers::*;
7use http::{
8 Extensions, HeaderMap, StatusCode,
9 header::{CONTENT_TYPE, HeaderName, HeaderValue},
10 response::Parts,
11};
12use topcoat_core::{
13 context::Cx,
14 error::{Error, Result},
15};
16
17use crate::{Body, BoxError};
18
19pub type Response<T = Body> = http::Response<T>;
20
21const TEXT_PLAIN: HeaderValue = HeaderValue::from_static("text/plain; charset=utf-8");
22const APPLICATION_OCTET_STREAM: HeaderValue = HeaderValue::from_static("application/octet-stream");
23
24pub trait IntoResponse {
70 fn into_response(self, cx: &Cx) -> Result<Response>;
78}
79
80pub trait AsyncIntoResponse {
88 fn async_into_response(self, cx: &Cx) -> impl Future<Output = Result<Response>> + Send;
95}
96
97impl<T: IntoResponse> AsyncIntoResponse for T {
98 fn async_into_response(self, cx: &Cx) -> impl Future<Output = Result<Response>> + Send {
99 ready(self.into_response(cx))
100 }
101}
102
103pub trait IntoResponseParts {
109 fn into_response_parts(self, cx: &Cx, parts: &mut Parts) -> Result<()>;
117}
118
119pub(crate) fn content_response(content_type: HeaderValue, body: Body) -> Response {
121 let mut response = Response::new(body);
122 response.headers_mut().insert(CONTENT_TYPE, content_type);
123 response
124}
125
126fn merge_parts(parts: &mut Parts, from: Parts) {
129 parts.status = from.status;
130 parts.version = from.version;
131 parts.headers.extend(from.headers);
132 parts.extensions.extend(from.extensions);
133}
134
135impl IntoResponse for Infallible {
138 fn into_response(self, _cx: &Cx) -> Result<Response> {
139 match self {}
140 }
141}
142
143impl IntoResponse for () {
144 fn into_response(self, _cx: &Cx) -> Result<Response> {
145 Ok(Response::new(Body::empty()))
146 }
147}
148
149impl IntoResponse for StatusCode {
150 fn into_response(self, cx: &Cx) -> Result<Response> {
151 (self, ()).into_response(cx)
152 }
153}
154
155impl IntoResponse for &'static str {
156 fn into_response(self, _cx: &Cx) -> Result<Response> {
157 Ok(content_response(TEXT_PLAIN, Body::from(self)))
158 }
159}
160
161impl IntoResponse for String {
162 fn into_response(self, _cx: &Cx) -> Result<Response> {
163 Ok(content_response(TEXT_PLAIN, Body::from(self)))
164 }
165}
166
167impl IntoResponse for Box<str> {
168 fn into_response(self, cx: &Cx) -> Result<Response> {
169 String::from(self).into_response(cx)
170 }
171}
172
173impl IntoResponse for Cow<'static, str> {
174 fn into_response(self, cx: &Cx) -> Result<Response> {
175 match self {
176 Cow::Borrowed(value) => value.into_response(cx),
177 Cow::Owned(value) => value.into_response(cx),
178 }
179 }
180}
181
182impl IntoResponse for &'static [u8] {
183 fn into_response(self, _cx: &Cx) -> Result<Response> {
184 Ok(content_response(APPLICATION_OCTET_STREAM, Body::from(self)))
185 }
186}
187
188impl<const N: usize> IntoResponse for &'static [u8; N] {
189 fn into_response(self, cx: &Cx) -> Result<Response> {
190 let bytes: &'static [u8] = self;
191 bytes.into_response(cx)
192 }
193}
194
195impl<const N: usize> IntoResponse for [u8; N] {
196 fn into_response(self, cx: &Cx) -> Result<Response> {
197 self.to_vec().into_response(cx)
198 }
199}
200
201impl IntoResponse for Vec<u8> {
202 fn into_response(self, _cx: &Cx) -> Result<Response> {
203 Ok(content_response(APPLICATION_OCTET_STREAM, Body::from(self)))
204 }
205}
206
207impl IntoResponse for Box<[u8]> {
208 fn into_response(self, cx: &Cx) -> Result<Response> {
209 Vec::from(self).into_response(cx)
210 }
211}
212
213impl IntoResponse for Cow<'static, [u8]> {
214 fn into_response(self, cx: &Cx) -> Result<Response> {
215 match self {
216 Cow::Borrowed(value) => value.into_response(cx),
217 Cow::Owned(value) => value.into_response(cx),
218 }
219 }
220}
221
222impl IntoResponse for Bytes {
223 fn into_response(self, _cx: &Cx) -> Result<Response> {
224 Ok(content_response(APPLICATION_OCTET_STREAM, Body::from(self)))
225 }
226}
227
228impl IntoResponse for BytesMut {
229 fn into_response(self, cx: &Cx) -> Result<Response> {
230 self.freeze().into_response(cx)
231 }
232}
233
234impl IntoResponse for Body {
235 fn into_response(self, _cx: &Cx) -> Result<Response> {
236 Ok(Response::new(self))
237 }
238}
239
240impl IntoResponse for HeaderMap {
241 fn into_response(self, cx: &Cx) -> Result<Response> {
242 (self, ()).into_response(cx)
243 }
244}
245
246impl IntoResponse for Extensions {
247 fn into_response(self, cx: &Cx) -> Result<Response> {
248 (self, ()).into_response(cx)
249 }
250}
251
252impl IntoResponse for Parts {
253 fn into_response(self, cx: &Cx) -> Result<Response> {
254 (self, ()).into_response(cx)
255 }
256}
257
258impl<K, V, const N: usize> IntoResponse for [(K, V); N]
260where
261 K: TryInto<HeaderName>,
262 K::Error: std::error::Error + Send + Sync + 'static,
263 V: TryInto<HeaderValue>,
264 V::Error: std::error::Error + Send + Sync + 'static,
265{
266 fn into_response(self, cx: &Cx) -> Result<Response> {
267 (self, ()).into_response(cx)
268 }
269}
270
271impl<B> IntoResponse for http::Response<B>
274where
275 B: http_body::Body<Data = Bytes> + Send + 'static,
276 B::Error: Into<BoxError>,
277{
278 fn into_response(self, _cx: &Cx) -> Result<Response> {
279 let (parts, body) = self.into_parts();
280 Ok(Response::from_parts(parts, Body::new(body)))
281 }
282}
283
284impl IntoResponseParts for () {
287 fn into_response_parts(self, _cx: &Cx, _parts: &mut Parts) -> Result<()> {
288 Ok(())
289 }
290}
291
292impl<T> IntoResponseParts for Option<T>
293where
294 T: IntoResponseParts,
295{
296 fn into_response_parts(self, cx: &Cx, parts: &mut Parts) -> Result<()> {
297 if let Some(value) = self {
298 value.into_response_parts(cx, parts)?;
299 }
300 Ok(())
301 }
302}
303
304impl IntoResponseParts for HeaderMap {
305 fn into_response_parts(self, _cx: &Cx, parts: &mut Parts) -> Result<()> {
306 parts.headers.extend(self);
307 Ok(())
308 }
309}
310
311impl IntoResponseParts for Extensions {
312 fn into_response_parts(self, _cx: &Cx, parts: &mut Parts) -> Result<()> {
313 parts.extensions.extend(self);
314 Ok(())
315 }
316}
317
318impl<K, V, const N: usize> IntoResponseParts for [(K, V); N]
321where
322 K: TryInto<HeaderName>,
323 K::Error: std::error::Error + Send + Sync + 'static,
324 V: TryInto<HeaderValue>,
325 V::Error: std::error::Error + Send + Sync + 'static,
326{
327 fn into_response_parts(self, _cx: &Cx, parts: &mut Parts) -> Result<()> {
328 for (name, value) in self {
329 let name = name.try_into().map_err(Error::from)?;
330 let value = value.try_into().map_err(Error::from)?;
331 parts.headers.insert(name, value);
332 }
333 Ok(())
334 }
335}
336
337macro_rules! impl_into_response_tuples {
346 ( $($ty:ident),* ) => {
347 #[allow(non_snake_case, unused_mut, unused_variables)]
348 impl<R, $($ty,)*> IntoResponse for ($($ty,)* R,)
349 where
350 R: IntoResponse,
351 $($ty: IntoResponseParts,)*
352 {
353 fn into_response(self, cx: &Cx) -> Result<Response> {
354 let ($($ty,)* r,) = self;
355 let (mut parts, body) = r.into_response(cx)?.into_parts();
356 $( $ty.into_response_parts(cx, &mut parts)?; )*
357 Ok(Response::from_parts(parts, body))
358 }
359 }
360
361 #[allow(non_snake_case, unused_mut, unused_variables)]
362 impl<R, $($ty,)*> IntoResponse for (StatusCode, $($ty,)* R,)
363 where
364 R: IntoResponse,
365 $($ty: IntoResponseParts,)*
366 {
367 fn into_response(self, cx: &Cx) -> Result<Response> {
368 let (status, $($ty,)* r,) = self;
369 let (mut parts, body) = r.into_response(cx)?.into_parts();
370 parts.status = status;
371 $( $ty.into_response_parts(cx, &mut parts)?; )*
372 Ok(Response::from_parts(parts, body))
373 }
374 }
375
376 #[allow(non_snake_case, unused_mut, unused_variables)]
377 impl<R, $($ty,)*> IntoResponse for (Parts, $($ty,)* R,)
378 where
379 R: IntoResponse,
380 $($ty: IntoResponseParts,)*
381 {
382 fn into_response(self, cx: &Cx) -> Result<Response> {
383 let (template, $($ty,)* r,) = self;
384 let (mut parts, body) = r.into_response(cx)?.into_parts();
385 merge_parts(&mut parts, template);
386 $( $ty.into_response_parts(cx, &mut parts)?; )*
387 Ok(Response::from_parts(parts, body))
388 }
389 }
390
391 #[allow(non_snake_case, unused_mut, unused_variables)]
392 impl<R, $($ty,)*> IntoResponse for (Response<()>, $($ty,)* R,)
393 where
394 R: IntoResponse,
395 $($ty: IntoResponseParts,)*
396 {
397 fn into_response(self, cx: &Cx) -> Result<Response> {
398 let (template, $($ty,)* r,) = self;
399 let (template, ()) = template.into_parts();
400 let (mut parts, body) = r.into_response(cx)?.into_parts();
401 merge_parts(&mut parts, template);
402 $( $ty.into_response_parts(cx, &mut parts)?; )*
403 Ok(Response::from_parts(parts, body))
404 }
405 }
406 };
407}
408
409impl_into_response_tuples!();
410impl_into_response_tuples!(T1);
411impl_into_response_tuples!(T1, T2);
412impl_into_response_tuples!(T1, T2, T3);
413impl_into_response_tuples!(T1, T2, T3, T4);
414impl_into_response_tuples!(T1, T2, T3, T4, T5);
415impl_into_response_tuples!(T1, T2, T3, T4, T5, T6);
416impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7);
417impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8);
418impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
419impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
420impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
421impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
422impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
423impl_into_response_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
424impl_into_response_tuples!(
425 T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
426);
427impl_into_response_tuples!(
428 T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
429);
430
431#[cfg(test)]
432mod tests {
433 use http_body_util::Full;
434
435 use super::*;
436 use crate::to_bytes;
437
438 fn block_on<F: Future>(future: F) -> F::Output {
439 tokio::runtime::Builder::new_current_thread()
440 .build()
441 .unwrap()
442 .block_on(future)
443 }
444
445 fn run(value: impl IntoResponse) -> (Parts, Bytes) {
447 let cx = Cx::default();
448 let (parts, body) = value.into_response(&cx).unwrap().into_parts();
449 let bytes = block_on(to_bytes(body, usize::MAX)).unwrap();
450 (parts, bytes)
451 }
452
453 fn header(parts: &Parts, name: &'static str) -> String {
454 parts
455 .headers
456 .get(name)
457 .unwrap()
458 .to_str()
459 .unwrap()
460 .to_owned()
461 }
462
463 #[test]
466 fn str_is_text_plain() {
467 let (parts, body) = run("hi");
468 assert_eq!(parts.status, StatusCode::OK);
469 assert_eq!(header(&parts, "content-type"), "text/plain; charset=utf-8");
470 assert_eq!(&body[..], b"hi");
471 }
472
473 #[test]
474 fn owned_and_borrowed_text_match() {
475 for (parts, body) in [
476 run(String::from("hi")),
477 run(Box::<str>::from("hi")),
478 run(Cow::Borrowed("hi")),
479 run(Cow::<str>::Owned("hi".to_owned())),
480 ] {
481 assert_eq!(header(&parts, "content-type"), "text/plain; charset=utf-8");
482 assert_eq!(&body[..], b"hi");
483 }
484 }
485
486 #[test]
487 fn byte_bodies_are_octet_stream() {
488 for (parts, body) in [
489 run(b"hi".to_vec()),
490 run(Bytes::from_static(b"hi")),
491 run(BytesMut::from(&b"hi"[..])),
492 run(*b"hi"),
493 run(b"hi"),
494 run(Box::<[u8]>::from(&b"hi"[..])),
495 ] {
496 assert_eq!(header(&parts, "content-type"), "application/octet-stream");
497 assert_eq!(&body[..], b"hi");
498 }
499 }
500
501 #[test]
502 fn unit_is_empty_ok() {
503 let (parts, body) = run(());
504 assert_eq!(parts.status, StatusCode::OK);
505 assert!(body.is_empty());
506 assert!(parts.headers.is_empty());
507 }
508
509 #[test]
510 fn status_code_sets_status_with_empty_body() {
511 let (parts, body) = run(StatusCode::NO_CONTENT);
512 assert_eq!(parts.status, StatusCode::NO_CONTENT);
513 assert!(body.is_empty());
514 }
515
516 #[test]
517 fn header_map_applies_headers() {
518 let mut headers = HeaderMap::new();
519 headers.insert(
520 HeaderName::from_static("x-test"),
521 HeaderValue::from_static("1"),
522 );
523 let (parts, body) = run(headers);
524 assert_eq!(header(&parts, "x-test"), "1");
525 assert!(body.is_empty());
526 }
527
528 #[test]
529 fn extensions_are_applied() {
530 #[derive(Clone, Debug, PartialEq)]
531 struct Marker(u32);
532
533 let mut extensions = Extensions::new();
534 extensions.insert(Marker(7));
535 let (parts, _) = run(extensions);
536 assert_eq!(parts.extensions.get::<Marker>(), Some(&Marker(7)));
537 }
538
539 #[test]
540 fn response_passes_through_with_rebodied_stream() {
541 let response = http::Response::builder()
542 .status(StatusCode::ACCEPTED)
543 .header("x-test", "1")
544 .body(Full::new(Bytes::from_static(b"yo")))
545 .unwrap();
546 let (parts, body) = run(response);
547 assert_eq!(parts.status, StatusCode::ACCEPTED);
548 assert_eq!(header(&parts, "x-test"), "1");
549 assert_eq!(&body[..], b"yo");
550 }
551
552 #[test]
555 fn header_array_is_into_response() {
556 let (parts, body) = run([("x-test", "1"), ("x-other", "2")]);
557 assert_eq!(header(&parts, "x-test"), "1");
558 assert_eq!(header(&parts, "x-other"), "2");
559 assert!(body.is_empty());
560 }
561
562 #[test]
563 fn invalid_header_name_is_an_error() {
564 let cx = Cx::default();
566 assert!([("inva lid", "1")].into_response(&cx).is_err());
567 }
568
569 #[test]
572 fn one_tuple_is_just_the_body() {
573 let (parts, body) = run(("hi",));
574 assert_eq!(parts.status, StatusCode::OK);
575 assert_eq!(&body[..], b"hi");
576 }
577
578 #[test]
579 fn status_then_body() {
580 let (parts, body) = run((StatusCode::CREATED, "hi"));
581 assert_eq!(parts.status, StatusCode::CREATED);
582 assert_eq!(header(&parts, "content-type"), "text/plain; charset=utf-8");
583 assert_eq!(&body[..], b"hi");
584 }
585
586 #[test]
587 fn headers_then_body() {
588 let (parts, body) = run(([("x-test", "1")], "hi"));
589 assert_eq!(header(&parts, "x-test"), "1");
590 assert_eq!(&body[..], b"hi");
591 }
592
593 #[test]
594 fn status_headers_and_body() {
595 let (parts, body) = run((StatusCode::CREATED, [("x-test", "1")], "hi"));
596 assert_eq!(parts.status, StatusCode::CREATED);
597 assert_eq!(header(&parts, "x-test"), "1");
598 assert_eq!(&body[..], b"hi");
599 }
600
601 #[test]
602 fn multiple_response_parts_are_all_applied() {
603 let (parts, body) = run((
604 StatusCode::CREATED,
605 [("x-one", "1")],
606 [("x-two", "2")],
607 "hi",
608 ));
609 assert_eq!(parts.status, StatusCode::CREATED);
610 assert_eq!(header(&parts, "x-one"), "1");
611 assert_eq!(header(&parts, "x-two"), "2");
612 assert_eq!(&body[..], b"hi");
613 }
614
615 #[test]
616 fn optional_parts_are_applied_when_present() {
617 let (present, _) = run((Some([("x-test", "1")]), "hi"));
618 assert_eq!(header(&present, "x-test"), "1");
619
620 let (absent, body) = run((Option::<[(&str, &str); 1]>::None, "hi"));
621 assert!(absent.headers.get("x-test").is_none());
622 assert_eq!(&body[..], b"hi");
623 }
624
625 #[test]
626 fn parts_template_seeds_status_and_headers() {
627 let (mut template, _) = Response::new(Body::empty()).into_parts();
628 template.status = StatusCode::IM_A_TEAPOT;
629 template.headers.insert(
630 HeaderName::from_static("x-test"),
631 HeaderValue::from_static("1"),
632 );
633
634 let (parts, body) = run((template, "hi"));
635 assert_eq!(parts.status, StatusCode::IM_A_TEAPOT);
636 assert_eq!(header(&parts, "x-test"), "1");
637 assert_eq!(&body[..], b"hi");
638 }
639
640 #[test]
641 fn response_unit_template_seeds_status_and_headers() {
642 let template = http::Response::builder()
643 .status(StatusCode::IM_A_TEAPOT)
644 .header("x-test", "1")
645 .body(())
646 .unwrap();
647
648 let (parts, body) = run((template, "hi"));
649 assert_eq!(parts.status, StatusCode::IM_A_TEAPOT);
650 assert_eq!(header(&parts, "x-test"), "1");
651 assert_eq!(&body[..], b"hi");
652 }
653}