Skip to main content

Crate typed_handlebars

Crate typed_handlebars 

Source
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 .hbs file 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 .hbs file in a directory, mirroring the directory layout.
file
Generates a module for a single .hbs file.
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
Render marker: written as it displays.
ViaOption
Render marker: written when Some, nothing when None.
ViaOptionRef
Render marker: as ViaOption, for an Option passed by reference.

Traits§

IsSet
Supplies the value held in a builder slot.
Length
How many items {{ rows.length }} reports.
Render
How a value is written by {{ }} and {{{ }}}.
RenderExt
Turns a value into something write! can take, escaped or not.
Truthy
Whether a value counts as true in {{#if}} and {{#unless}}.