finchers_template/
askama.rs

1//! Components for supporting askama.
2
3extern crate askama;
4
5#[doc(no_inline)]
6pub use self::askama::Template;
7pub use self::imp::{renderer, RenderEndpoint, Renderer};
8
9mod imp {
10    use super::askama::Template;
11
12    use finchers::endpoint::wrapper::Wrapper;
13    use finchers::endpoint::{ApplyContext, ApplyResult, Endpoint};
14    use finchers::error;
15    use finchers::error::Error;
16
17    use futures::{Future, Poll};
18    use http::header;
19    use http::header::HeaderValue;
20    use http::Response;
21    use mime;
22    use mime::Mime;
23
24    /// Create a `Renderer` for rendering the value of context type which implements
25    /// `askama::Template`.
26    pub fn renderer() -> Renderer {
27        Renderer {
28            content_type: mime::TEXT_HTML_UTF_8.as_ref().parse().unwrap(),
29        }
30    }
31
32    /// The type for modifying the result rendered by Askama to an HTTP response.
33    #[derive(Debug)]
34    pub struct Renderer {
35        content_type: HeaderValue,
36    }
37
38    impl Renderer {
39        /// Sets the content type of generated HTTP response.
40        ///
41        /// The default value is `text/html; charset=utf-8`.
42        pub fn content_type(self, content_type: Mime) -> Renderer {
43            let content_type = content_type
44                .as_ref()
45                .parse()
46                .expect("the MIME value should be a valid header value");
47            Renderer {
48                content_type,
49                ..self
50            }
51        }
52
53        fn render_response<T>(&self, ctx: &T) -> Result<Response<String>, super::askama::Error>
54        where
55            T: Template,
56        {
57            let mut response = ctx.render().map(Response::new)?;
58            response
59                .headers_mut()
60                .insert(header::CONTENT_TYPE, self.content_type.clone());
61            Ok(response)
62        }
63    }
64
65    impl<'a, E, CtxT> Wrapper<'a, E> for Renderer
66    where
67        E: Endpoint<'a, Output = (CtxT,)>,
68        CtxT: Template,
69    {
70        type Output = (Response<String>,);
71        type Endpoint = RenderEndpoint<E>;
72
73        fn wrap(self, endpoint: E) -> Self::Endpoint {
74            RenderEndpoint {
75                endpoint,
76                renderer: self,
77            }
78        }
79    }
80
81    /// An endpoint which renders the output of inner endpoint and
82    /// convert it into an HTTP response.
83    #[derive(Debug)]
84    pub struct RenderEndpoint<E> {
85        endpoint: E,
86        renderer: Renderer,
87    }
88
89    impl<'a, E, CtxT> Endpoint<'a> for RenderEndpoint<E>
90    where
91        E: Endpoint<'a, Output = (CtxT,)>,
92        CtxT: Template,
93    {
94        type Output = (Response<String>,);
95        type Future = RenderFuture<'a, E>;
96
97        fn apply(&'a self, cx: &mut ApplyContext<'_>) -> ApplyResult<Self::Future> {
98            Ok(RenderFuture {
99                future: self.endpoint.apply(cx)?,
100                endpoint: self,
101            })
102        }
103    }
104
105    // not a public API.
106    #[derive(Debug)]
107    pub struct RenderFuture<'a, E: Endpoint<'a>> {
108        future: E::Future,
109        endpoint: &'a RenderEndpoint<E>,
110    }
111
112    impl<'a, E, CtxT> Future for RenderFuture<'a, E>
113    where
114        E: Endpoint<'a, Output = (CtxT,)>,
115        CtxT: Template,
116    {
117        type Item = (Response<String>,);
118        type Error = Error;
119
120        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
121            let (ctx,) = try_ready!(self.future.poll());
122            self.endpoint
123                .renderer
124                .render_response(&ctx)
125                .map(|response| (response,).into())
126                .map_err(error::fail)
127        }
128    }
129}