A simple, powerful template engine.
{{ user.name }}
{% if user.enabled %} ... {% endif %}
{% for user in users %} ... {% endfor %}
<? 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 }
Your entry point is the Engine
struct. The engine stores the syntax
config, filter functions, 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!");
Here is the same example as above except using derived data.
```rust
struct Context { user: User }
struct User { name: String }
let ctx = Context { user: User { name: "John Smith".into() } };
let result = upon::Engine::new() .compile("Hello {{ user.name }}")? .render(&ctx)?;
assert_eq!(result, "Hello John Smith"); ```
Data can be transformed using registered filters.
```rust let mut engine = upon::Engine::new(); engine.addfilter("lower", str::tolowercase);
let result = engine .compile("Hello {{ value | lower }}")? .render(upon::value! { value: "WORLD!" })?;
assert_eq!(result, "Hello world!"); ```
See the Filter
trait documentation for more information on filters.
The template syntax can be set by constructing an engine using
Engine::with_syntax
.
```rust let syntax = upon::Syntax::builder().expr("", "?>").block("<%", "%>").build();
let result = upon::Engine::with_syntax(syntax) .compile("Hello user.name ?>")? .render(upon::value!{ user: { name: "John Smith" }})?;
assert_eq!(result, "Hello John Smith"); ```
impl io::Write
You can render a template directly to a buffer implementing io::Write
by using .render_to_writer()
.
```rust use std::io;
let stdout = io::BufWriter::new(io::stdout());
upon::Engine::new() .compile("Hello {{ user.name }}")? .rendertowriter(stdout, upon::value! { user: { name: "John Smith" }})?; ```
You can add your own custom formatter’s or even override the default
formatter using Engine::set_default_formatter
. The following example
shows how you could add debug
formatter to the engine.
```rust use std::fmt::Write; use upon::{Formatter, Value, Result};
let mut engine = upon::Engine::new(); engine.add_formatter("debug", |f, value| { write!(f, "Value::{:?}", value)?; Ok(()) });
let result = engine .compile("User age: {{ user.age | debug }}")? .render(upon::value! { user: { age: 23 } })?;
assert_eq!(result, "User age: Value::Integer(23)"); ```
The following shows a violin plot of the benchmark results for upon
compared
to the following template rendering engines.
- handlebars
- liquid
- minijinja
- tera
- tinytemplate
Obviously, each of these engines has a completely different feature set so this just compares the performance of some of the features that they share.
Host - MacBook Pro (14-inch, 2021) - Chipset: Apple M1 Pro - Memory: 16 GB
Licensed under either of
at your option.