Bart is a compile time templating language for Rust inspired by Mustache. It plays to Rust's strengths by statically compiling the template into efficient code and performing full variable resolution and type checking at compile time.
To use Bart, add these dependencies to your Cargo.toml
:
[dependencies]
bart = "0.1.4"
bart_derive = "0.1.4"
Given the template file hello_world.html
:
Hello {{name}}
We can write the following program:
#[derive(bart_derive::BartDisplay)]
#[template = "hello_world.html"]
struct HelloWorld<'a> {
name: &'a str,
}
fn main() {
print!("{}", &HelloWorld { name: "World" });
}
To compile this example program, you need to add both bart
and bart_derive
as dependencies in your Cargo.toml
.
Running this program will output
Hello World
You can run this example by cloning this repository and executing cargo run --example hello_world
.
#[derive(BartDisplay)]
The programmer interface to Bart is the procedural macro defined in the bart_derive
crate, which implements support for #[derive(bart_derive::BartDisplay)]
. It must be added as a dependency in your Cargo.toml
. bart_derive
generates code which is dependent on the bart
crate, so you also need to pull this in as a dependency.
Use bart_derive::BartDisplay
to generate an impl
of the Display
trait based on the template and struct below.
#[template = "hello_world.html"]
bart_derive
will read hello_world.html
and use it to generate the template rendering code. The given file name is relative to your crate root, so, for example, you have to specify #[template = "src/hello_world.html"]
if you want your template to reside in the src/
directory.
It is also possible to specify the template inline with template_string
: #[template_string = "Hello {{name}}"]
.
struct HelloWorld<'a> {
name: &'a str,
}
Values to be interpolated in the template will be resolved from the given struct
. In this case {{name}}
would be resolved to the name
field of this struct. Fields to be interpolated must implement the Display
trait.
fn main() {
print!("{}", &HelloWorld { name: "World" });
}
As noted above, bart_derive
has now generated an impl
of Display
for HelloWorld
. This means we can pass instances of HelloWorld
to print!
, write!
, format!
and so on. The template is rendered with the supplied data, generating Hello World
to standard output.
The Bart templating language is inspired by Mustache. (Bart is the Norwegian word for Mustache.)
The input is reproduced verbatim except for tags. Tags start with {{
and end with }}
.
The simplest tag is the interpolation tag, which contains a data reference. For the template Hello {{name}}
, {{name}}
is recognized as an interpolation tag and name
is resolved as a field on the given struct
. This field must implement the Display
trait. It is possible to use .
to refer to fields in nested struct
s; {{name.surname}}
.
Interpolation tags are HTML escaped, so for the template Hello {{name}}
, if {{name}}
is Bobby <tags>
, the output will be Hello Bobby <tags>
.
It is also useful to be able to deliberately include HTML content unescaped. Use triple-tags, {{{
…}}}
, for this: Hello {{{name}}}
would render Hello Bobby <tags>
if name
were Bobby <tags>
.
It is possible to iterate over anything that implements IntoIterator
:
<ul>
{{#values}}
<li>{{.}}</li>
{{/values}}
</ul>
Use {{.}}
to refer to the current value. For example, if values
were a Vec<i32>
, {{.}}
would refer to each of the contained i32
values in turn. When iterating over a set of structures, use a .
prefix to refer to members:
<ul>
{{#people}}
<li>{{.name}} ({{.age}})</li>
{{/people}}
</ul>
It can be useful to take advantage of the IntoIterator
implementations on Option
and Result
to use them in Bart iterations.
Similar to iteration, it is possible to enter a scope for a variable, by specifying a trailing dot:
{{#person.}}
{{.name}} ({{.age}})
{{/person}}
It is also possible to fully qualify each reference:
{{person.name}} ({{person.age}})
When in a nested scope, use multiple leading dots to step out:
{{#department.}}
{{#head.}}
{{.name}}, head of the {{..name}} department.
{{/head}}
{{/department}}
Unqualified names, that is, names without leading dots, will always be resolved in the topmost scope.
The same scoping rules apply to iteration scopes.