somni-template 0.4.0

A configurable templating engine built on the Somni expression language
Documentation

somni-template

A small, configurable templating engine built on top of somni-expr.

Templates are transpiled into a Somni program and executed: literal text is emitted verbatim, while {{ expr }} interpolations, if/for conditions and loop iterables are evaluated by somni-expr.

Features

  • Configurable syntax. Interpolation delimiters and block-directive style are both configurable. Block directives can be a delimiter pair (e.g. {% ... %}, /* ... */) or a line prefix (e.g. #, //). An optional text line prefix (e.g. //>) is stripped from matching lines so the remainder is emitted as text — useful when the template is also a valid program and conditional output must stay commented if run directly. Optional ----fenced frontmatter at the start of a template may override the syntax passed to Template::compile (frontmatter wins for keys it sets).
  • Control flow. if / else if / else and for <var> in <iterable> (with an optional <var>: <type> annotation), arbitrarily nestable.
  • Strongly typed output. Interpolation is string-only; convert other types explicitly (a generic str conversion is provided, more can be registered).
  • Errors point into the template, even for evaluation errors in the generated program.

Example

use somni_template::{Env, Iter, Syntax, Template};

let tmpl = Template::compile(
    "Hello, {{ name }}!\n#for n in nums\n- {{ str(n) }}\n#endfor\n",
    &Syntax::lines(),
)
.unwrap();

let mut env = Env::new();          // ships a default `str` conversion
env.value("name", "Ada");
env.value("nums", Iter(vec![1u64, 2, 3]));

let out = tmpl.render(env).unwrap();
assert_eq!(out, "Hello, Ada!\n- 1\n- 2\n- 3\n");

Notes

  • Interpolation must evaluate to a string; use {{ str(x) }} (or a custom conversion) for other types.
  • for needs a host-provided iterable. The loop-variable type annotation is optional (for x in ...); when omitted it is inferred from how the variable is used, but it can still be given explicitly (for x: int in ...).
  • Iterables registered with [Iter] are single-pass. To re-iterate a source (e.g. an inner loop nested in an outer loop), register a host function that returns a fresh iterator and call it in the loop header (#for x: int in items()).

License

Licensed under either of Apache-2.0 or MIT, at your option.