micro_web/
responder.rs

1//! Response handling module that converts handler results into HTTP responses.
2//!
3//! This module provides the [`Responder`] trait which defines how different types
4//! can be converted into HTTP responses. It includes implementations for common types
5//! like Result, Option, String, etc.
6//!
7//! The [`Responder`] trait is a key part of the response pipeline, allowing handler
8//! return values to be automatically converted into proper HTTP responses.
9
10pub mod sse;
11
12use crate::RequestContext;
13use crate::body::ResponseBody;
14use http::{Response, StatusCode};
15use std::convert::Infallible;
16
17/// A trait for types that can be converted into HTTP responses.
18///
19/// Types implementing this trait can be returned directly from request handlers
20/// and will be automatically converted into HTTP responses.
21pub trait Responder {
22    fn response_to(self, req: &RequestContext) -> Response<ResponseBody>;
23}
24
25/// Implementation for Result allows handlers to return Result types directly.
26/// The Ok and Err variants must both implement Responder.
27impl<T: Responder, E: Responder> Responder for Result<T, E> {
28    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
29        match self {
30            Ok(t) => t.response_to(req),
31            Err(e) => e.response_to(req),
32        }
33    }
34}
35
36/// Implementation for Option allows handlers to return Option types.
37/// None case returns an empty response.
38impl<T: Responder> Responder for Option<T> {
39    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
40        match self {
41            Some(t) => t.response_to(req),
42            None => Response::new(ResponseBody::empty()),
43        }
44    }
45}
46
47/// Implementation for Response allows passing through pre-built responses.
48/// The response body is converted to the internal ResponseBody type.
49impl<B> Responder for Response<B>
50where
51    B: Into<ResponseBody>,
52{
53    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
54        self.map(|b| b.into())
55    }
56}
57
58/// Implementation for (StatusCode, T) tuple allows setting a status code
59/// along with the response content.
60impl<T: Responder> Responder for (StatusCode, T) {
61    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
62        let (status, responder) = self;
63        let mut response = responder.response_to(req);
64        *response.status_mut() = status;
65        response
66    }
67}
68
69/// Implementation for (T, StatusCode) tuple - same as above but with reversed order.
70impl<T: Responder> Responder for (T, StatusCode) {
71    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
72        let (responder, status) = self;
73        (status, responder).response_to(req)
74    }
75}
76
77/// Implementation for Box<T> allows boxing responders.
78impl<T: Responder> Responder for Box<T> {
79    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
80        (*self).response_to(req)
81    }
82}
83
84/// Implementation for unit type () returns an empty response.
85impl Responder for () {
86    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
87        Response::new(ResponseBody::empty())
88    }
89}
90
91/// Implementation for static strings returns them as plain text responses.
92impl Responder for &'static str {
93    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
94        let mut builder = Response::builder();
95        let headers = builder.headers_mut().unwrap();
96        headers.reserve(8);
97        headers.insert(http::header::CONTENT_TYPE, mime::TEXT_PLAIN_UTF_8.as_ref().parse().unwrap());
98
99        builder.status(StatusCode::OK).body(ResponseBody::from(self)).unwrap()
100    }
101}
102
103/// Implementation for String returns it as a plain text response.
104impl Responder for String {
105    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
106        let mut builder = Response::builder();
107        let headers = builder.headers_mut().unwrap();
108        headers.reserve(8);
109        headers.insert(http::header::CONTENT_TYPE, mime::TEXT_PLAIN_UTF_8.as_ref().parse().unwrap());
110
111        builder.status(StatusCode::OK).body(ResponseBody::from(self)).unwrap()
112    }
113}
114
115impl Responder for Infallible {
116    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
117        unreachable!()
118    }
119}
120
121pub struct NotFound;
122
123impl Responder for NotFound {
124    #[inline]
125    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
126        ("404 Not Found.", StatusCode::NOT_FOUND).response_to(req)
127    }
128}