#![cfg_attr(finchers_inject_extern_prelude, feature(extern_prelude))]
#![doc(html_root_url = "https://docs.rs/finchers-template/0.1.1")]
#![warn(
missing_docs,
missing_debug_implementations,
nonstandard_style,
rust_2018_idioms,
unused,
)]
#![cfg_attr(finchers_deny_warnings, deny(warnings))]
#![cfg_attr(finchers_deny_warnings, doc(test(attr(deny(warnings)))))]
extern crate failure;
extern crate finchers;
#[macro_use]
extern crate futures;
extern crate http;
extern crate mime;
extern crate mime_guess;
extern crate serde;
#[cfg(feature = "handlebars")]
extern crate handlebars;
#[cfg(feature = "tera")]
extern crate tera;
#[allow(deprecated)]
pub use self::imp::TemplateEndpoint;
pub use self::imp::{renderer, RenderEndpoint, Renderer, TemplateEngine};
#[cfg(feature = "handlebars")]
#[doc(no_inline)]
pub use handlebars::Handlebars;
#[cfg(feature = "tera")]
#[doc(no_inline)]
pub use tera::Tera;
#[cfg(feature = "askama")]
pub mod askama;
#[cfg(feature = "horrorshow")]
pub mod horrorshow;
mod imp {
use finchers;
use finchers::endpoint;
use finchers::endpoint::wrapper::Wrapper;
use finchers::endpoint::{ApplyContext, ApplyResult, Endpoint, IntoEndpoint};
use finchers::error::Error;
use finchers::output::body::ResBody;
#[cfg(feature = "handlebars")]
use handlebars::Handlebars;
#[cfg(feature = "tera")]
use tera::Tera;
use failure::SyncFailure;
use futures::{Future, Poll};
use http::header;
use http::header::HeaderValue;
use http::Response;
use mime::Mime;
use mime_guess::guess_mime_type;
use serde::Serialize;
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
#[allow(missing_docs)]
pub trait TemplateEngine {
type Body: ResBody;
type Error: Into<Error>;
fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
where
T: Serialize;
}
impl<E: TemplateEngine> TemplateEngine for Box<E> {
type Body = E::Body;
type Error = E::Error;
fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
where
T: Serialize,
{
(**self).render(template_name, ctx)
}
}
impl<E: TemplateEngine> TemplateEngine for Rc<E> {
type Body = E::Body;
type Error = E::Error;
fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
where
T: Serialize,
{
(**self).render(template_name, ctx)
}
}
impl<E: TemplateEngine> TemplateEngine for Arc<E> {
type Body = E::Body;
type Error = E::Error;
fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
where
T: Serialize,
{
(**self).render(template_name, ctx)
}
}
#[cfg(feature = "handlebars")]
impl TemplateEngine for Handlebars {
type Body = String;
type Error = Error;
fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
where
T: Serialize,
{
Handlebars::render(self, template_name, ctx)
.map_err(|err| finchers::error::fail(SyncFailure::new(err)))
}
}
#[cfg(feature = "tera")]
impl TemplateEngine for Tera {
type Body = String;
type Error = Error;
fn render<T>(&self, template_name: &str, ctx: &T) -> Result<Self::Body, Self::Error>
where
T: Serialize,
{
Tera::render(self, template_name, ctx)
.map_err(|err| finchers::error::fail(SyncFailure::new(err)))
}
}
pub fn renderer<T>(engine: T, name: impl Into<Cow<'static, str>>) -> Renderer<T>
where
T: TemplateEngine,
{
let name = name.into();
let content_type = HeaderValue::from_shared(guess_mime_type(&*name).as_ref().into())
.expect("should be a valid header value");
Renderer {
engine,
name,
content_type,
}
}
#[derive(Debug, Clone)]
pub struct Renderer<T> {
engine: T,
name: Cow<'static, str>,
content_type: HeaderValue,
}
impl<T> Renderer<T>
where
T: TemplateEngine,
{
#[doc(hidden)]
#[deprecated(note = "use `renderer()` instead.")]
pub fn new(engine: T, name: impl Into<Cow<'static, str>>) -> Renderer<T> {
renderer(engine, name)
}
pub fn content_type(self, content_type: Mime) -> Renderer<T> {
Renderer {
content_type: HeaderValue::from_shared(content_type.as_ref().into())
.expect("should be a valid header value"),
..self
}
}
fn render_response<CtxT>(&self, ctx: &CtxT) -> Result<Response<T::Body>, Error>
where
CtxT: Serialize,
{
let mut response = self
.engine
.render(&self.name, ctx)
.map(Response::new)
.map_err(Into::into)?;
response
.headers_mut()
.insert(header::CONTENT_TYPE, self.content_type.clone());
Ok(response)
}
}
impl<'a, T> IntoEndpoint<'a> for Renderer<T>
where
T: TemplateEngine + 'a,
{
type Output = (Response<T::Body>,);
type Endpoint = RenderEndpoint<T, endpoint::Cloned<self::dummy::DummyContext>>;
fn into_endpoint(self) -> Self::Endpoint {
RenderEndpoint {
renderer: self,
endpoint: endpoint::cloned(Default::default()),
}
}
}
mod dummy {
use serde::ser::{Serialize, SerializeMap, Serializer};
#[derive(Debug, Default, Clone, Copy)]
pub struct DummyContext {
_priv: (),
}
impl Serialize for DummyContext {
fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
ser.serialize_map(Some(0))?.end()
}
}
}
impl<'a, T, E, CtxT> Wrapper<'a, E> for Renderer<T>
where
T: TemplateEngine + 'a,
E: Endpoint<'a, Output = (CtxT,)>,
CtxT: Serialize,
{
type Output = (Response<T::Body>,);
type Endpoint = RenderEndpoint<T, E>;
fn wrap(self, endpoint: E) -> Self::Endpoint {
RenderEndpoint {
renderer: self,
endpoint,
}
}
}
#[doc(hidden)]
#[deprecated(since = "0.1.1", note = "renamed to `RenderEndpoint<T, E>")]
pub type TemplateEndpoint<T, E> = RenderEndpoint<T, E>;
#[derive(Debug)]
pub struct RenderEndpoint<T, E> {
renderer: Renderer<T>,
endpoint: E,
}
impl<'a, T, E, CtxT> Endpoint<'a> for RenderEndpoint<T, E>
where
T: TemplateEngine + 'a,
E: Endpoint<'a, Output = (CtxT,)>,
CtxT: Serialize,
{
type Output = (Response<T::Body>,);
type Future = TemplateFuture<'a, T, E>;
#[inline]
fn apply(&'a self, cx: &mut ApplyContext<'_>) -> ApplyResult<Self::Future> {
Ok(TemplateFuture {
future: self.endpoint.apply(cx)?,
renderer: &self.renderer,
})
}
}
#[derive(Debug)]
pub struct TemplateFuture<'a, T: TemplateEngine + 'a, E: Endpoint<'a>> {
future: E::Future,
renderer: &'a Renderer<T>,
}
impl<'a, T, E, CtxT> Future for TemplateFuture<'a, T, E>
where
T: TemplateEngine + 'a,
E: Endpoint<'a, Output = (CtxT,)>,
CtxT: Serialize,
{
type Item = (Response<T::Body>,);
type Error = Error;
#[inline]
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let (ctx,) = try_ready!(self.future.poll());
self.renderer
.render_response(&ctx)
.map(|response| (response,).into())
}
}
}