Expand description
§gotmpl
A Rust port of Go’s text/template.
Supports the full template syntax (pipelines, control flow, custom functions, template
composition, whitespace trimming) with Go compatible output. no_std compatible (with alloc).
The crate forbids unsafe code and denies the panic-family lints (panic!, unwrap,
expect, unreachable!, todo!, unimplemented!).
User-provided functions that panic are caught under std; see no_std notes below.
§Quick start
use gotmpl::{Template, tmap};
let data = tmap! { "Name" => "World" };
let result = Template::new("hello")
.parse("Hello, {{.Name}}!")
.unwrap()
.execute_to_string(&data)
.unwrap();
assert_eq!(result, "Hello, World!");For one-shot renders with no configuration, use gotmpl::execute (source
string) or gotmpl::execute_file (reads from disk):
use gotmpl::{execute, tmap};
let result = execute("Hello, {{.Name}}!", &tmap! { "Name" => "World" }).unwrap();
assert_eq!(result, "Hello, World!");§Template syntax
Actions are delimited by {{ and }} (configurable via .delims()).
§Data access
{{.}} Current context (dot)
{{.Name}} Field access on dot
{{.User.Email}} Nested field access
{{$}} Top-level data (root context)
{{$x}} Variable access
{{$x.Name}} Field access on variable§Pipelines
{{.Name | printf "%s!"}}
{{"hello" | len | printf "%d chars"}}§Control flow
{{if .Condition}}...{{end}}
{{if .Cond}}...{{else}}...{{end}}
{{if eq .X 1}}...{{else if eq .X 2}}...{{else}}...{{end}}
{{range .Items}}...{{end}}
{{range .Items}}...{{else}}empty{{end}}
{{range $i, $v := .Items}}{{$i}}: {{$v}}{{end}}
{{range .Items}}{{if eq . 3}}{{break}}{{end}}{{.}}{{end}}
{{range .Items}}{{if eq . 3}}{{continue}}{{end}}{{.}}{{end}}
{{range 5}}{{.}} {{end}}
{{with .Value}}...{{end}}
{{with .Value}}...{{else}}fallback{{end}}§Variables
{{$x := .Name}} Declare variable
{{$x = "new value"}} Assign to existing variable
{{$i, $v := range .List}} Range with index and value (declaration)
{{$i, $v = range .List}} Range with index and value (assignment)§Template composition
{{define "name"}}...{{end}} Define a named template
{{template "name" .}} Invoke a named template
{{block "name" .}}default{{end}} Define and invoke (with default)§Comments and whitespace trimming
{{/* This is a comment */}}
{{- .X}} Trim whitespace before
{{.X -}} Trim whitespace after
{{- .X -}} Trim both sides§Built-in functions
| Function | Description |
|---|---|
print | Concatenate args (spaces between non-string adjacent args) |
printf | Formatted output (see below) |
println | Print with spaces between args, trailing newline |
len | Length of string, list, or map |
index | Index into list or map: index .List 0, index .Map "key" |
slice | Slice a list or string: slice .List 1 3 |
call | Call a function value: call .Func arg1 arg2 |
eq, ne, lt, le, gt, ge | Comparison operators. eq supports multi-arg: eq .X 1 2 3 |
and, or | Short-circuit logic, return the deciding value |
not | Boolean negation |
html, js, urlquery | Escape for HTML, JavaScript, URL query |
§printf verbs and flags
Format strings follow Go’s fmt syntax:
%[flags][width][.precision][argument index]verb.
| Verb | Applies to | Output |
|---|---|---|
%s | string | The string itself |
%q | string, int(rune) | Go-quoted string, or single-quoted rune literal |
%v | any | Default formatted value |
%d | int | Decimal |
%b | int | Binary |
%o | int | Octal |
%x, %X | int, string | Lower/upper hex (on strings: hex of each byte) |
%c | int | Unicode scalar |
%U | int | Unicode U+XXXX (with #: appends 'c') |
%f | float | Decimal, no exponent |
%e, %E | float | Scientific notation (lower/upper e) |
%g, %G | float | %e/%E for large exponents, else %f |
%t | bool | true / false |
%% | — | Literal % |
Flags: - (left-align), + (always sign numerics), (leading space for non-negative
numerics), # (alternate form: 0b/0o/0x/0X prefix, or quoted rune for %U),
0 (zero-pad numerics).
Width and .precision accept either a literal number or * / .* to read the value
from the next argument (which must be an int — floats and other types yield
%!(BADWIDTH) / %!(BADPREC)).
Argument indexing with %[N]verb selects the N-th (1-based) argument and resets the
sequential cursor. Out-of-range or malformed indices produce %!verb(BADINDEX).
Mismatched verb/argument pairs produce Go’s error markers rather than panicking:
%!v(BADVERB), %!v(MISSING), %!(EXTRA …) for unconsumed args, and the
BADWIDTH / BADPREC / BADINDEX forms above.
§Custom functions
Register functions before parsing:
use gotmpl::{Template, tmap};
use gotmpl::Value;
let result = Template::new("test")
.func("upper", |args| {
match args.first() {
Some(Value::String(s)) => Ok(Value::String(s.to_uppercase().into())),
_ => Ok(Value::Nil),
}
})
.parse("{{.Name | upper}}")
.unwrap()
.execute_to_string(&tmap! { "Name" => "hello" })
.unwrap();
assert_eq!(result, "HELLO");§Function values and call
Value::Function allows passing callable values through templates:
extern crate alloc;
use alloc::sync::Arc;
use gotmpl::{Template, tmap};
use gotmpl::{Value, ValueFunc};
let adder: ValueFunc = Arc::new(|args| {
let sum: i64 = args.iter().filter_map(|a| a.as_int()).sum();
Ok(Value::Int(sum))
});
let result = Template::new("test")
.func("getAdder", move |_| Ok(Value::Function(adder.clone())))
.parse("{{call (getAdder) 3 4}}")
.unwrap()
.execute_to_string(&tmap!{})
.unwrap();
assert_eq!(result, "7");§Options
use gotmpl::{Template, MissingKey};
let tmpl = Template::new("t")
.missing_key(MissingKey::Error) // error on missing map keys
.delims("<<", ">>") // custom delimiters
.parse("<< .Name >>")
.unwrap();MissingKey implements FromStr, so you can parse from strings (useful for
config files or CLI args):
use gotmpl::MissingKey;
let mk: MissingKey = "error".parse().unwrap();MissingKey variant | FromStr value | Behavior |
|---|---|---|
Invalid (default) | "invalid", "default" | Return <no value> |
ZeroValue | "zero" | Return <no value> |
Error | "error" | Return an error |
ZeroValue exists for parity with Go’s {{options "missingkey=zero"}} directive.
Since Value is untyped, it behaves the same as Invalid; the variant is there so
callers can still opt in to the named option.
§Number literals
Go-compatible number literal syntax:
{{42}} Decimal
{{3.14}} Float
{{0xFF}} Hexadecimal
{{0o77}} Octal
{{0377}} Octal (legacy leading zero)
{{0b1010}} Binary
{{1_000_000}} Underscore separators
{{'a'}} Character literal (97)
{{0x1.ep+2}} Hex float (7.5)§Data model
Template data uses the Value enum:
| Variant | Rust type | Go equivalent |
|---|---|---|
Nil | n/a | nil |
Bool(bool) | bool | bool |
Int(i64) | i64 | int |
Float(f64) | f64 | float64 |
String(Arc<str>) | String | string |
List(Arc<[Value]>) | Vec<Value> | []any |
Map(Arc<BTreeMap<Arc<str>, Value>>) | BTreeMap | map[string]any |
Function(ValueFunc) | Arc<dyn Fn> | func(...) |
The tmap! macro builds data maps:
use gotmpl::{tmap, ToValue};
let data = tmap! {
"Name" => "Alice",
"Age" => 30i64,
"Scores" => vec![95i64, 87, 92],
"Address" => tmap! {
"City" => "Paris",
},
};§no_std support
The crate works in no_std environments (requires alloc). Disable the default std
feature:
[dependencies]
gotmpl = { version = "0.3", default-features = false }Without std, execute_fmt and execute_to_string are available. The io::Write-based
execute/execute_template methods and parse_files require the std feature.
User-defined functions that panic will propagate instead of being caught.
§Differences from Go
Rust has no runtime reflection, so:
- No struct field access: use
Value::Mapinstead - No method calls: register functions via
.func() - No pointer/interface indirection:
Valueis always concrete - No complex numbers, channels, or
iter.Seq - NaN comparisons return an error instead of Go’s silently wrong results
API shape is also a bit different:
Template::lookupreturns the parsed body (Option<&ListNode>) rather than a re-executable*Template. To run a named definition, useexecute_template_to_string("name", ...)on the parent.Template::templatesreturns the list of defined names, notTemplateobjects — definitions share the parent’s func map and options, so there’s no per-definition handle to hand back.parse_filesrequires the files to be valid UTF-8. Go’sos.ReadFile+string(b)is a zero-copy reinterpret and accepts any bytes; we usestd::fs::read_to_string, which validates.
A few formatting and slicing edge cases diverge too:
%#vfalls back to%v. Go’s Go-syntax output ([]interface {}{1, 2, 3},map[string]interface {}{"a":1}) needs concrete-type info we don’t carry.%#Uquotes some non-ASCII codepoints that Go skips. Our gate ischar::is_control; Go uses the fullstrconv.IsPrinttable, which also rejects non-ASCII spacing characters like U+00A0.sliceon a string at a mid-codepoint offset returns an error. Go would hand back the raw bytes (potentially invalid UTF-8); we can’t store invalid UTF-8 inValue::String, so we reject the slice instead.
§Go cross-check
The test suite can optionally run every template through Go’s text/template and assert
output parity:
cargo test --features go-crosscheckA Go helper (tests/testdata/go_crosscheck.go) is compiled once per test run. It
reads templates and typed data from stdin as JSON, executes them via Go’s
text/template, and prints the result.
Modules§
- parse
- Parser, lexer, and AST node types for the Go template language.
Macros§
- tmap
- Creates a
Value::Mapfrom key-value pairs, similar to Go’s map literals.
Structs§
- Template
- A parsed template, equivalent to Go’s
template.Template.
Enums§
- Missing
Key - Controls behavior when accessing a missing key on a
Value::Map. Set viaTemplate::missing_key. - Template
Error - The error type returned by all template operations.
- Value
- The dynamic type for template data.
Traits§
Functions§
- execute
- Parse and execute a template in one shot.
- execute_
file std - Parse a template file and execute it in one shot.
- html_
escape - HTML-escape a string, replacing
&,<,>,",', and NUL bytes. - is_true
- Reports whether a
Valueis “true” according to Go’s template truthiness rules. - js_
escape - JavaScript-escape a string for safe embedding in JS string literals.
- url_
encode - Percent-encode a string for use in URL query parameters (form-encoding).
Type Aliases§
- FuncMap
- A map from function names to template functions, equivalent to Go’s
template.FuncMap. Pass one toTemplate::funcsto register several functions at once. - Result
- Alias for
Result<T, TemplateError>, the return type of every fallible operation in this crate. - Value
Func - Alias for a callable function stored inside
Value::Function.