finchers_core/output/
responder.rs

1use std::borrow::Cow;
2use std::fmt;
3
4use bytes::Bytes;
5use either::Either;
6use http::header::HeaderValue;
7use http::{header, Response};
8
9use super::body::ResponseBody;
10use error::Error;
11use input::Input;
12
13const TEXT_PLAIN: &str = "text/plain; charset=utf-8";
14
15/// A type alias of value which will be returned from `Responder`.
16pub type Output = Response<ResponseBody>;
17
18/// Trait representing the conversion to an HTTP response.
19pub trait Responder {
20    /// Consume `self` and construct a new HTTP response.
21    fn respond(self, input: &Input) -> Result<Output, Error>;
22}
23
24impl<T> Responder for Response<T>
25where
26    T: Into<ResponseBody>,
27{
28    fn respond(self, _: &Input) -> Result<Output, Error> {
29        Ok(self.map(Into::into))
30    }
31}
32
33impl Responder for &'static str {
34    fn respond(self, _: &Input) -> Result<Output, Error> {
35        Ok(make_response(Bytes::from_static(self.as_bytes()), TEXT_PLAIN))
36    }
37}
38
39impl Responder for String {
40    fn respond(self, _: &Input) -> Result<Output, Error> {
41        Ok(make_response(Bytes::from(self), TEXT_PLAIN))
42    }
43}
44
45impl Responder for Cow<'static, str> {
46    fn respond(self, _: &Input) -> Result<Output, Error> {
47        let body = match self {
48            Cow::Borrowed(s) => Bytes::from_static(s.as_bytes()),
49            Cow::Owned(s) => Bytes::from(s),
50        };
51        Ok(make_response(body, TEXT_PLAIN))
52    }
53}
54
55impl<T, E> Responder for Result<T, E>
56where
57    T: Responder,
58    E: Into<Error>,
59{
60    fn respond(self, input: &Input) -> Result<Output, Error> {
61        self.map_err(Into::<Error>::into)?.respond(input).map_err(Into::into)
62    }
63}
64
65impl<L, R> Responder for Either<L, R>
66where
67    L: Responder,
68    R: Responder,
69{
70    fn respond(self, input: &Input) -> Result<Output, Error> {
71        match self {
72            Either::Left(l) => l.respond(input),
73            Either::Right(r) => r.respond(input),
74        }
75    }
76}
77
78/// A helper struct for creating the response from types which implements `fmt::Debug`.
79///
80/// NOTE: This wrapper is only for debugging and should not use in the production code.
81#[derive(Debug)]
82pub struct Debug {
83    value: Box<fmt::Debug + Send + 'static>,
84    pretty: bool,
85}
86
87impl Debug {
88    /// Create an instance of `Debug` from an value whose type has an implementation of
89    /// `fmt::Debug`.
90    pub fn new<T>(value: T) -> Debug
91    where
92        T: fmt::Debug + Send + 'static,
93    {
94        Debug {
95            value: Box::new(value),
96            pretty: false,
97        }
98    }
99
100    /// Set whether this responder uses the pretty-printed specifier (`"{:#?}"`) or not.
101    pub fn pretty(mut self, enabled: bool) -> Self {
102        self.pretty = enabled;
103        self
104    }
105}
106
107impl Responder for Debug {
108    fn respond(self, _: &Input) -> Result<Output, Error> {
109        let body = if self.pretty {
110            format!("{:#?}", self.value)
111        } else {
112            format!("{:?}", self.value)
113        };
114
115        let mut response = Response::new(ResponseBody::once(body));
116        content_type(&mut response, TEXT_PLAIN);
117        content_length(&mut response);
118
119        Ok(response)
120    }
121}
122
123fn make_response(body: Bytes, m: &'static str) -> Output {
124    let mut response = Response::new(ResponseBody::once(body));
125    content_type(&mut response, m);
126    content_length(&mut response);
127    response
128}
129
130fn content_type<T>(response: &mut Response<T>, value: &'static str) {
131    response
132        .headers_mut()
133        .insert(header::CONTENT_TYPE, HeaderValue::from_static(value));
134}
135
136fn content_length(response: &mut Response<ResponseBody>) {
137    if let Some(body_len) = response.body().len() {
138        response.headers_mut().insert(header::CONTENT_LENGTH, unsafe {
139            HeaderValue::from_shared_unchecked(Bytes::from(body_len.to_string()))
140        });
141    }
142}