2 releases
0.0.2 | Mar 5, 2019 |
---|---|
0.0.1 | Feb 24, 2019 |
#69 in #handlebars
Used in wearte
105KB
3K
SLoC
[WIP] wearte
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 <p> 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 <p> Tags</h1>
<div class="body">
<p>This is a post about <p> 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
~17–28MB
~412K SLoC