A lightweight and powerful template engine for Rust.
{{ user.name }}
{% if user.enabled %} ... {% endif %}
{% for user in users %} ... {% endfor %}
{% include "nested" %}
<? user.name ?>
, (( if user.enabled ))
{{ user.name | replace: "\t", " " }}
{{ user.name | escape_html }}
String
or any std::io::Write
implementorserde
serializable valuesupon::value!{ name: "John", age: 42 }
{:#}
It’s true there are already a lot of template engines for Rust!
I created upon
because I required a template engine that had runtime
compiled templates, configurable syntax delimiters and minimal dependencies.
I also didn’t need support for arbitrary expressions in the template syntax
but occasionally I needed something more flexible than outputting simple
values (hence filters). Performance was also a concern for me, template
engines like [Handlebars] and [Tera] have a lot of features but can be up to
five to seven times slower to render than engines like [TinyTemplate].
Basically I wanted something like [TinyTemplate] with support for configurable delimiters and user defined filter functions. The syntax is inspired by template engines like [Liquid] and [Jinja].
Currently the minimum supported version for upon
is Rust 1.60. The policy
of this crate is to only increase the MSRV in a breaking release.
First, add the crate to your Cargo manifest.
sh
cargo add upon
Now construct an Engine
. The engine stores the syntax config, filter
functions, formatters, and compiled templates. Generally, you only need to
construct one engine during the lifetime of a program.
rust
let engine = upon::Engine::new();
Next, add_template
is used to compile and store a
template in the engine.
rust
engine.add_template("hello", "Hello {{ user.name }}!")?;
Finally, the template is rendered by fetching it using
get_template
and calling
render
.
rust
let template = engine.get_template("hello").unwrap();
let result = template.render(upon::value!{ user: { name: "John Smith" }})?;
assert_eq!(result, "Hello John Smith!");
If the lifetime of the template source is shorter than the engine lifetime
or you don’t need to store the compiled template then you can also use the
compile
function to return the template directly.
rust
let template = engine.compile("Hello {{ user.name }}!")?;
let result = template.render(upon::value!{ user: { name: "John Smith" }})?;
assert_eq!(result, "Hello John Smith!");
syntax
module documentation outlines the template syntax.filters
module documentation describes filters and how they work.fmt
module documentation contains information on value formatters.examples/
directory in the repository contains concrete
code examples.The following crate features are available.
filters
(enabled by default) — Enables support for filters in
templates (see Engine::add_filter
). This does not affect value
formatters (see Engine::add_formatter
). Disabling this will improve
compile times.
serde
(enabled by default) — Enables all serde support and pulls
in the serde
crate as a dependency. If disabled then you can use
render_from
to render templates and
construct the context using Value
’s From
impls.
unicode
(enabled by default) — Enables unicode support and pulls
in the unicode-ident
and
unicode-width
crates. If disabled then unicode
identifiers will no longer be allowed in templates and .chars().count()
will be used in error formatting.
To disable all features or to use a subset you need to set default-features = false
in your Cargo manifest and then enable the features that you would
like. For example to use serde
but disable filters
and
unicode
you would do the following.
toml
[dependencies]
upon = { version = "...", default-features = false, features = ["serde"] }
upon
was benchmarked against several popular template rendering engines in the
Rust ecosystem. Obviously, each of these engines has a completely different
feature set so the benchmark just compares the performance of some of the
features that they share. Handlebars is so slow that it is excluded from the
compile violin plot.
Benchmarking was done using criterion on a quiet cloud machine.
Host
Licensed under either of
at your option.