Expand description
A mustache template engine supporting complex expressions and statements.
use pochoir_lang::Context;
use pochoir_template_engine::process_template;
let mut context = Context::new();
context.insert("name", "world");
assert_eq!(
process_template("index.html", "Hello {{ name }}!", &mut context, 0),
Ok("Hello world!".to_string()),
);§Variables and expressions
Variables needs to be defined in a Context. They can be inserted using Context::insert
but if you want child components to also inherit the data (like a global variable), you should
use Context::insert_inherited. Both functions take a key as first argument which is the name
of the variable usable in expressions and a value implementing the IntoValue trait
(implemented for a lot of default types and structures). The value will then be cloned and
transformed in each expression. If you want to pass enumerations or structures, you should
implement IntoValue on them using the IntoValue derive macro. The variable keys must
follow some rules to be valid: they must only contain lowercase and uppercase Latin letters,
underscores and digits (but the first character must not be a digit).
When data needs to be inserted inside a page, you need to use an expression. Expressions are tiny groups of variables and operators written in their own custom language that manipulate data. They are written in a pair of curly brackets and can be used everywhere you write text. The resulting value of expressions is escaped to prevent XSS attacks, but it is possible to opt out of auto-escaping by replacing the inner curly brackets by exclamation marks.
§Example
use pochoir_lang::Context;
use pochoir_template_engine::process_template;
let mut context = Context::new();
context.insert("date", "August 26, 2023");
context.insert("html", "<b>some bold HTML</b>");
let source = "
Today it is {{ date }}.
Unescaped HTML can be inserted: {! html !}.";
let expected = "
Today it is August 26, 2023.
Unescaped HTML can be inserted: <b>some bold HTML</b>.";
assert_eq!(
process_template(
"index.html",
source,
&mut context,
0,
),
Ok(expected.to_string()),
);§Statements
Common statements can be used in templates. They are all written in curly brackets with inner percentages.
if/elif/elseconditional statements are used to check if an expression equalstrue. If it does, the inner content will be included, if not it won’t. One additional feature is that you can use theif let(andelif let) syntax to check if a value is not null. It can be combined with an assignment to replicate theif let Some(_) = _syntax of Rust: “unwrap” the value if it is not null or don’t execute a block content at all if the value is null. You need to note that assignments return the assigned value (like in Javascript) which enables theif letsyntax to do that.forloop statements are also supported. They are mostly used to iterate lists so they are written using thefor ... in ...syntax. If you want to just get some ordered numbers, you would need to use ranges likefor ... in 3..12. Destructuring objects and arrays is also supported, so if you iterate an array of objects and they all share the same structure you can bind their the fields to comma-separated variables instead of indexing them later. The same thing is supported for arrays, except that you can name the keys as you want, just the order in which they are defined is important. If a value cannot be destructured (because the field does not exist), the value will simply benull.- Finally,
letstatements can be used to assign a value to a variable and comments can be added with curly brackets with inner#s.
§Example
use pochoir_lang::{object, Context};
use pochoir_template_engine::process_template;
let mut context = Context::new();
context.insert("date", "August 26, 2023");
context.insert("weather", "cloudy");
context.insert("users", vec![
object! {
"name" => "John",
"job" => "Football player"
},
object! {
"name" => "Jane",
"job" => "Designer",
}
]);
let source = r#"
{% if date == "August 26, 2023" %}
It is today!
{% elif date == "August 25, 2023" %}
It is yesterday!
{% else %}
Oh welcome to the future.
{% endif %}
{# Note that a single `=` is used here because it is an assignment. If `weather` is null, the
assignment will return `null` and the `if` block content will never run (because `if let`
checks for `null`ity) but if the value is something other than `null`, `current_weather` will
be different than `null` and the inner block will be run #}
{% if let current_weather = weather %}
Today it is {{ current_weather }}.
{% endif %}
{# We now define a variable in the template and iterate it in a for loop #}
{% let alphabet = ["a", "b", "c", "d"] %}
{% for letter in alphabet %}"{{ letter }}" then {% endfor %}"z"
{# Objects are destructured here: the `name` and `job` fields will be extracted from the
objects to avoid having to later index them #}
<ul>
{% for name, job in users %}
<li>{{ name }} is a {{ job }}</li>
{% endfor %}
</ul>"#;
let expected = r#"
It is today!
Today it is cloudy.
"a" then "b" then "c" then "d" then "z"
<ul>
<li>John is a Football player</li>
<li>Jane is a Designer</li>
</ul>"#;
assert_eq!(
process_template(
"index.html",
source,
&mut context,
0,
),
Ok(expected.to_string()),
);§Custom parsing
To extend the behavior of parsing template expressions and statements, it is possible to use
the TemplateCustomParsing trait which will let you customize the behavior of the parser for
each character encountered. It is especially used in pochoir-parser to evaluate expressions
in element attributes where it is useful to stop the parsing when " is found.
§Quirks
Nested objects defined in template expressions need to have spaces between their curly braces to differentiate them from template expression delimiters.
Bad:
{{ {a: {b: "letters"}} }}The error will be:
error: unterminated object
./index.html:1:7
1 | {{ {a:{b: "letters"}} }}
Fixed:
{{ {a: {b: "letters"} } }}Re-exports§
pub use escaping::Escaping;
Modules§
Enums§
Traits§
Functions§
- parse_
template - Parse a template to a list of
TemplateBlocks along with their span. - process_
template - Parse and render a template string.
- render_
template - Render a previously-parsed template to a string.
- stream_
parse_ template - Parse a template to a list of
TemplateBlocks along with their span from a pre-builtStreamParser.