finchers_template/
lib.rs

1// FIXME: remove this feature gate as soon as the rustc version used in docs.rs is updated
2#![cfg_attr(finchers_inject_extern_prelude, feature(extern_prelude))]
3
4//! Template support for Finchers
5
6#![doc(html_root_url = "https://docs.rs/finchers-template/0.1.1")]
7#![warn(
8    missing_docs,
9    missing_debug_implementations,
10    nonstandard_style,
11    rust_2018_idioms,
12    unused,
13)]
14//#![warn(rust_2018_compatibility)]
15#![cfg_attr(finchers_deny_warnings, deny(warnings))]
16#![cfg_attr(finchers_deny_warnings, doc(test(attr(deny(warnings)))))]
17
18extern crate failure;
19extern crate finchers;
20#[macro_use]
21extern crate futures;
22extern crate http;
23extern crate mime;
24extern crate mime_guess;
25extern crate serde;
26
27#[cfg(feature = "handlebars")]
28extern crate handlebars;
29
30#[cfg(feature = "tera")]
31extern crate tera;
32
33#[allow(deprecated)]
34pub use self::imp::TemplateEndpoint;
35pub use self::imp::{renderer, RenderEndpoint, Renderer, TemplateEngine};
36
37#[cfg(feature = "handlebars")]
38#[doc(no_inline)]
39pub use handlebars::Handlebars;
40
41#[cfg(feature = "tera")]
42#[doc(no_inline)]
43pub use tera::Tera;
44
45#[cfg(feature = "askama")]
46pub mod askama;
47
48#[cfg(feature = "horrorshow")]
49pub mod horrorshow;
50
51mod imp {
52    use finchers;
53    use finchers::endpoint;
54    use finchers::endpoint::wrapper::Wrapper;
55    use finchers::endpoint::{ApplyContext, ApplyResult, Endpoint, IntoEndpoint};
56    use finchers::error::Error;
57    use finchers::output::body::ResBody;
58
59    #[cfg(feature = "handlebars")]
60    use handlebars::Handlebars;
61    #[cfg(feature = "tera")]
62    use tera::Tera;
63
64    use failure::SyncFailure;
65    use futures::{Future, Poll};
66    use http::header;
67    use http::header::HeaderValue;
68    use http::Response;
69    use mime::Mime;
70    use mime_guess::guess_mime_type;
71    use serde::Serialize;
72
73    use std::borrow::Cow;
74    use std::rc::Rc;
75    use std::sync::Arc;
76
77    /// A trait representing template engine used in `Renderer`.
78    #[allow(missing_docs)]
79    pub trait TemplateEngine {
80        type Body: ResBody;
81        type Error: Into<Error>;
82
83        fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
84        where
85            T: Serialize;
86    }
87
88    impl<E: TemplateEngine> TemplateEngine for Box<E> {
89        type Body = E::Body;
90        type Error = E::Error;
91
92        fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
93        where
94            T: Serialize,
95        {
96            (**self).render(template_name, ctx)
97        }
98    }
99
100    impl<E: TemplateEngine> TemplateEngine for Rc<E> {
101        type Body = E::Body;
102        type Error = E::Error;
103
104        fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
105        where
106            T: Serialize,
107        {
108            (**self).render(template_name, ctx)
109        }
110    }
111
112    impl<E: TemplateEngine> TemplateEngine for Arc<E> {
113        type Body = E::Body;
114        type Error = E::Error;
115
116        fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
117        where
118            T: Serialize,
119        {
120            (**self).render(template_name, ctx)
121        }
122    }
123
124    #[cfg(feature = "handlebars")]
125    impl TemplateEngine for Handlebars {
126        type Body = String;
127        type Error = Error;
128
129        fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
130        where
131            T: Serialize,
132        {
133            Handlebars::render(self, template_name, ctx)
134                .map_err(|err| finchers::error::fail(SyncFailure::new(err)))
135        }
136    }
137
138    #[cfg(feature = "tera")]
139    impl TemplateEngine for Tera {
140        type Body = String;
141        type Error = Error;
142
143        fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
144        where
145            T: Serialize,
146        {
147            Tera::render(self, template_name, ctx)
148                .map_err(|err| finchers::error::fail(SyncFailure::new(err)))
149        }
150    }
151
152    /// Create a new `Renderer` from the specified template engine and template name.
153    pub fn renderer<T>(engine: T, name: impl Into<Cow<'static, str>>) -> Renderer<T>
154    where
155        T: TemplateEngine,
156    {
157        let name = name.into();
158        let content_type = HeaderValue::from_shared(guess_mime_type(&*name).as_ref().into())
159            .expect("should be a valid header value");
160        Renderer {
161            engine,
162            name,
163            content_type,
164        }
165    }
166
167    /// The type representing a renderer using the specified template engine.
168    #[derive(Debug, Clone)]
169    pub struct Renderer<T> {
170        engine: T,
171        name: Cow<'static, str>,
172        content_type: HeaderValue,
173    }
174
175    impl<T> Renderer<T>
176    where
177        T: TemplateEngine,
178    {
179        #[doc(hidden)]
180        #[deprecated(note = "use `renderer()` instead.")]
181        pub fn new(engine: T, name: impl Into<Cow<'static, str>>) -> Renderer<T> {
182            renderer(engine, name)
183        }
184
185        /// Set the content-type of generated content.
186        ///
187        /// By default, the value is guessed from the name of template.
188        pub fn content_type(self, content_type: Mime) -> Renderer<T> {
189            Renderer {
190                content_type: HeaderValue::from_shared(content_type.as_ref().into())
191                    .expect("should be a valid header value"),
192                ..self
193            }
194        }
195
196        /// Renders a template using the specified context value.
197        fn render_response<CtxT>(&self, ctx: &CtxT) -> Result<Response<T::Body>, Error>
198        where
199            CtxT: Serialize,
200        {
201            let mut response = self
202                .engine
203                .render(&self.name, ctx)
204                .map(Response::new)
205                .map_err(Into::into)?;
206            response
207                .headers_mut()
208                .insert(header::CONTENT_TYPE, self.content_type.clone());
209            Ok(response)
210        }
211    }
212
213    impl<'a, T> IntoEndpoint<'a> for Renderer<T>
214    where
215        T: TemplateEngine + 'a,
216    {
217        type Output = (Response<T::Body>,);
218        type Endpoint = RenderEndpoint<T, endpoint::Cloned<self::dummy::DummyContext>>;
219
220        fn into_endpoint(self) -> Self::Endpoint {
221            RenderEndpoint {
222                renderer: self,
223                endpoint: endpoint::cloned(Default::default()),
224            }
225        }
226    }
227
228    mod dummy {
229        use serde::ser::{Serialize, SerializeMap, Serializer};
230
231        #[derive(Debug, Default, Clone, Copy)]
232        pub struct DummyContext {
233            _priv: (),
234        }
235
236        impl Serialize for DummyContext {
237            fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
238            where
239                S: Serializer,
240            {
241                ser.serialize_map(Some(0))?.end()
242            }
243        }
244    }
245
246    impl<'a, T, E, CtxT> Wrapper<'a, E> for Renderer<T>
247    where
248        T: TemplateEngine + 'a,
249        E: Endpoint<'a, Output = (CtxT,)>,
250        CtxT: Serialize,
251    {
252        type Output = (Response<T::Body>,);
253        type Endpoint = RenderEndpoint<T, E>;
254
255        fn wrap(self, endpoint: E) -> Self::Endpoint {
256            RenderEndpoint {
257                renderer: self,
258                endpoint,
259            }
260        }
261    }
262
263    #[doc(hidden)]
264    #[deprecated(since = "0.1.1", note = "renamed to `RenderEndpoint<T, E>")]
265    pub type TemplateEndpoint<T, E> = RenderEndpoint<T, E>;
266
267    /// The type of endpoint which renders a Tera template with the value of specified context type.
268    #[derive(Debug)]
269    pub struct RenderEndpoint<T, E> {
270        renderer: Renderer<T>,
271        endpoint: E,
272    }
273
274    impl<'a, T, E, CtxT> Endpoint<'a> for RenderEndpoint<T, E>
275    where
276        T: TemplateEngine + 'a,
277        E: Endpoint<'a, Output = (CtxT,)>,
278        CtxT: Serialize,
279    {
280        type Output = (Response<T::Body>,);
281        type Future = TemplateFuture<'a, T, E>;
282
283        #[inline]
284        fn apply(&'a self, cx: &mut ApplyContext<'_>) -> ApplyResult<Self::Future> {
285            Ok(TemplateFuture {
286                future: self.endpoint.apply(cx)?,
287                renderer: &self.renderer,
288            })
289        }
290    }
291
292    #[derive(Debug)]
293    pub struct TemplateFuture<'a, T: TemplateEngine + 'a, E: Endpoint<'a>> {
294        future: E::Future,
295        renderer: &'a Renderer<T>,
296    }
297
298    impl<'a, T, E, CtxT> Future for TemplateFuture<'a, T, E>
299    where
300        T: TemplateEngine + 'a,
301        E: Endpoint<'a, Output = (CtxT,)>,
302        CtxT: Serialize,
303    {
304        type Item = (Response<T::Body>,);
305        type Error = Error;
306
307        #[inline]
308        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
309            let (ctx,) = try_ready!(self.future.poll());
310            self.renderer
311                .render_response(&ctx)
312                .map(|response| (response,).into())
313        }
314    }
315}