2 releases

0.0.2 Mar 5, 2019
0.0.1 Feb 24, 2019

#60 in #handlebars


Used in wearte

MIT/Apache

7KB
175 lines

[WIP] wearte Documentation Latest version Build status Downloads

wearte stands for Wow Even Another Rust Template Engine, it is one of the fastest rust template engines. It uses a Handlebars-like syntax.

This crate was forked from yarte with fixes for the snarky licensing issues. yarte itself is a direct descendant of askama. You can find copies of their licenses in LICENSE-MIT.

Why a derive template engine?

There are many templates engines based on mustache or/and handlebars, I have not known any that derives the compilation of templates to the compiler (like askama). By deriving this task from another process, we can optimize the instructions generated with our own tools or those of third parties such as LLVM. This is impossible in other cases creating a bottleneck in our web servers that reaches milliseconds. Because of this, wearte puts the template in priority by allocating its needs statically. Thus, we write faster than the macro write!, easy parallelism and with simd in its default html escape.

In conclusion a derive is used to be the fastest and simplest.

Getting started

Add wearte dependency to your Cargo.toml file:

[dependencies]
wearte = "0.0.1"

In order to use a struct in the template you will have to call the procedural macro Template. For example, in the following code we are going to use struct CardTemplate, to then define s as a CardTemplate with content.

use wearte::Template;

#[derive(Template)]
#[template(path = "hello.html")]
struct CardTemplate<'a> {
    title: &'a str,
    body: &'a str,
}

let template = CardTemplate {
    title: "My Title",
    body: "My Body",
};

Now that our struct is defined lets use it in a template. wearte templates look like regular text, with embedded wearte expressions.

Let's say file hello.html looks like this:

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>

And call your template for allocate the result in String and return it wrapped with wearte::Result

template.call()
<div class="entry">
  <h1> My Title </h1>
  <div class="body">
    My Body
  </div>
</div>

Templating

wearte uses opening characters {{ and closing characters }} to parse the inside depending on the feature used. Most of the features are defined by Handlebars such as paths, comments, html, helpers and partials. Others such as adding rust code to a template, are obviously defined by wearte.

// precompile your template
#[derive(Template)]
#[template(source = "Hello, {{ name }}!", ext = "txt")]
struct HelloTemplate<'a> {
    name: &'a str,
}

assert_eq!(
    "Hello, world!", 
    HelloTemplate { name: "world" }.call().unwrap() // then call it.
); 

Comments

{{!   Comments can be written  }}
{{!--  in two different ways --}}

HTML

wearte HTML-escapes values returned by a {{expression}}. If you don't want wearte to escape a value, use the "triple-stash", {{{. For example having the following struct:

let t = CardTemplate {
  title: "All about <p> Tags",
  body: "<p>This is a post about &lt;p&gt; tags</p>"
};

and the following template:

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{{body}}}
  </div>
</div>

will result in:

<div class="entry">
  <h1>All About &lt;p&gt; Tags</h1>
  <div class="body">
    <p>This is a post about &lt;p&gt; tags</p>
  </div>
</div>

Helpers

Built-in

If, else, and else if helper
{{#if isLiked}}
  Liked!
{{else if isSeen}}
  Seen!
{{else}}
  Sorry ...
{{\if}}
With helper
let author = Author {
    name: "J. R. R. Tolkien"
};
{{#with author}}
  <p>{{name}}</p>
{{/with}}
Each helper
{{#each into_iter}} 
    {{#- if first || last -}}
        {{ index }} 
    {{- else -}}
        {{ index0 }} 
    {{/-if }} {{ key }} 
{{\-each}}
Unless helper
{{#unless isAdministrator-}} 
  Ask administrator.
{{\-unless}}
[WIP] Log helper
{{#log }} {{log\}}
[WIP] Lookup helper
{{#lookup }} {{\lookup}}

[WIP] User-defined

In order to create a user-defined helper ..

Literal

Booleans, integers, floating points, ... are not escaped for better performance. It is recomended to use these types if possible.

Partials

Partials can be used to generate faster code using a pre defined functions.

{{> path/to/file }}

Rust code

wearte provides you with the possibility to use raw rust code within the HTML files. This is limited, but most of essential syntax is supported.

{{#with getUser(id)?-}}
    Hello, {{#if isAdmin || isDev }}Mr. {{\if}}{{ user }}
{{/-with}}
Hello, {{#each conditions}}
    {{#-if let Some(check) = cond }}
        {{#-if check }}
            {{ let cond = if check { "&foo" } else { "&"} }}
            {{
                if check {
                    cond
                } else if let Some(cond) = key.cond {
                    if cond {
                        "1"
                    } else {
                        "2"
                    }
                } else {
                   "for"
                }
            }}
        {{- else if let Some(_) = cond }}
        {{- else if let Some(cond) = key.check }}
            {{#-if cond -}}
                baa
            {{/-if }}
        {{- else -}}
            {{ cond.is_some() }}
        {{/-if-}}
        {{ cond.is_some() && true }}
    {{-else if let Some(cond) = check }}
        {{#-if cond -}}
            bar
        {{/-if}}
    {{- else -}}
        None
    {{/-if
}}{{/each}}!
{{ let mut a = name.chars() }}

{{  
    let b: String = loop {
        if a.next().is_none() && true {
            let mut a = name.repeat(1);
            a.push('!');
            break a.repeat(2);
        } else {
            continue;
        }
    }
}}

{{ b }}
{{ let doubled = a.iter().map(|x| x * 2).collect::<Vec<_>>() }}
{{ let doubled: Vec<usize> = a.iter().map(|x| x * 2).collect() }}

{{#each doubled -}}
    {{ key + 1 }}
{{/-each}}

Roadmap

  • Minimize html5 at literal
  • Derive builders for generate defined helpers and filters
  • >| filters on fmt::Formatter
  • Concatenate filters, unix like, on fmt::Formatter (when is possible)
  • ... you can open a issue!

Dependencies

~2.5MB
~56K SLoC