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::{HeaderValue, 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
91const TEXT_PLAIN_CONTENT_TYPE: HeaderValue = HeaderValue::from_static("text/plain; charset=utf-8");
92
93/// Implementation for static strings returns them as plain text responses.
94impl Responder for &'static str {
95    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
96        let mut builder = Response::builder();
97        let headers = builder.headers_mut().unwrap();
98        headers.reserve(16);
99        headers.insert(http::header::CONTENT_TYPE, TEXT_PLAIN_CONTENT_TYPE);
100
101        builder.status(StatusCode::OK).body(ResponseBody::from(self)).unwrap()
102    }
103}
104
105/// Implementation for String returns it as a plain text response.
106impl Responder for String {
107    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
108        let mut builder = Response::builder();
109        let headers = builder.headers_mut().unwrap();
110        headers.reserve(16);
111        headers.insert(http::header::CONTENT_TYPE, TEXT_PLAIN_CONTENT_TYPE);
112        builder.status(StatusCode::OK).body(ResponseBody::from(self)).unwrap()
113    }
114}
115
116impl Responder for Infallible {
117    fn response_to(self, _req: &RequestContext) -> Response<ResponseBody> {
118        unreachable!()
119    }
120}
121
122#[derive(Debug)]
123pub struct NotFound;
124
125impl Responder for NotFound {
126    #[inline]
127    fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
128        ("404 Not Found.", StatusCode::NOT_FOUND).response_to(req)
129    }
130}