Expand description
Compile-time checked Handlebars templates for Rust.
Your .hbs files are turned into Rust when the crate is built, so there is no parsing, no
template registry and no lookups at run time. The types a template needs are generated from
what the template itself says, so there is nothing to declare, derive or implement.
See the project README for the goals, the full table of supported Handlebars constructs, and worked examples of partials and nesting.
§Example
Given templates/button.hbs:
<button id="btn{{ btn_id }}">{{ btn_name }}</button>directory! turns each file into a module holding a Vars — every variable the template
uses, named. It is an ordinary struct, so you write it as a literal:
mod templates {
// The README uses "templates/"; this crate keeps its doc fixtures here.
typed_handlebars::directory!("doc-templates/");
}
assert_eq!(
templates::button::Vars { btn_id: 42, btn_name: "Save" }.render(),
r#"<button id="btn42">Save</button>"#
);Nothing depends on argument order, your IDE offers the names, and the compiler checks them: a
misspelled field names the one you meant, and a variable added to the .hbs breaks every call
site rather than quietly rendering as nothing.
When you do not have every variable, builder() sets the ones you do have and leaves the rest
empty — as an undefined variable is in Handlebars:
assert_eq!(
templates::button::builder().btn_id(42).render(),
r#"<button id="btn42"></button>"#
);§Entry points
directory!— a module per.hbsfile in a folder, mirroring the directory layout. Resolves{{> partials}}against that tree.file!— a single template file.str!— a template written inline, for a one-liner or a test. No directory, so no partials.
A mistake in a template is reported against the .hbs file with a line and column, in
Handlebars terms; anything outside the supported subset is a compile error naming the
construct, never a silent difference in output.
§Rendering
render() returns a String, and render_to writes into any fmt::Write
sink, so a buffer you already have needs no throwaway String:
use core::fmt::Write;
let mut page = String::from("<div>");
templates::button::Vars { btn_id: 42, btn_name: "Save" }
.render_to(&mut page)
.unwrap();
page.push_str("</div>");
assert_eq!(page, r#"<div><button id="btn42">Save</button></div>"#);{{ name }} is HTML-escaped and {{{ name }}} is not, as Handlebars specifies. Markup you
have already rendered goes in {{{ }}} — which is how one template’s output is nested inside
another, exactly as handlebars.js passes a rendered fragment in as a variable.
A variable can be an Option, and None writes nothing — as null and undefined do in
handlebars.js — so a nullable column needs no unwrapping on the way in:
let missing: Option<&str> = None;
assert_eq!(
templates::button::Vars { btn_id: 42, btn_name: missing }.render(),
r#"<button id="btn42"></button>"#
);§Items in this crate
Apart from the three macros, everything here — Empty, Absent, Render,
RenderExt, Escaped, Shown, Truthy, Length, Set and IsSet — is
runtime support that generated code calls into. It is public because the generated code names
it, not because you need to: there is nothing here for you to implement.
Macros§
- directory
- Generates a module per
.hbsfile in a directory, mirroring the directory layout. - file
- Generates a module for a single
.hbsfile. - register_
helper - Names the frame: the type whose methods a template’s helper calls resolve on.
- str
- Generates a module from a template written inline, given a name and the template text.
Structs§
- Absent
- A list variable that was never given a value.
- Empty
- A variable that was never given a value.
- Escaped
- The HTML-escaping wrapper produced by
RenderExt::escaped. - Set
- A value a builder has been given.
- Shown
- The wrapper produced by
RenderExt::shown. - ViaDisplay
Rendermarker: written as it displays.- ViaOption
Rendermarker: written whenSome, nothing whenNone.- ViaOption
Ref Rendermarker: asViaOption, for anOptionpassed by reference.