Native implementation of {{mustache}} templates in Odin.
demo.mp4
All features are implemented, except for the ability to change delimiters.
All in tests in the official mustache spec pass successfully (except for the delimiters spec suite).
For more information about mustache, see the mustache project page or the mustache man pages.
View some example mustache files to get an overview.
The mustache-spec repo is added as a git submodule for testing purposes. To run tests, ensure that you run git clone --recursive
and/or git submodule update
as needed.
Usage:
odin-mustache [path to template] [path to JSON] [OPTIONAL - layout]
Examples:
$ odin-mustache template.txt data.json
Renders a template, provided as a string
. data
and partials
should be either a map[string]...
or a struct
.
All map
arguments passed must be keyed with string
type data. When parsing a Mustache template, the text inside a tag (eg. name
inside {{name}}
) will be parsed as a string
.
Renders a template stored in a text file using data
and partials
provided.
Renders a template string
using data and partials stored inside a JSON file. odin-mustache
will handle loading the JSON into a usable format for Mustache to work with.
NOTE: The JSON file leverages the following top-level keys:
"data": [required]
"partials": [optional]
Renders a template stored in a text file using data and partials stored inside a JSON file. odin-mustache
will handle loading the JSON into a usable format for Mustache to work with.
input := "Hello, {{name}}!"
data: map[string]string = {
"name" = "St. Charles",
}
output, err := render(input, data)
// => "Hello, St. Charles!"
odin-mustache
follows the official mustache HTML escaping rules. That is, if you enclose a variable with two curly brackets, {{var}}
, the contents are HTML-escaped. For instance, strings like 5 > 2
are converted to 5 > 2
. To use raw characters, use three curly brackets {{{var}}}
.
odin-mustache
supports rendering templates with layouts.
A layout is a regular template with one restriction: layout files can only have a single {{content}}
tag.
Layouts can render content in both {{normal}}
and {{{literal}}}
tags.
<html>
<h1>My Website</h1>
<body>{{{content}}}</body>
</html>
Usage:
odin-mustache [path to template] [path to JSON] [OPTIONAL - layout]
Examples:
$ odin-mustache template.html data.json layout.html
To render a layout in Odin, all four of the above render procedures have in_layout
and in_layout_file
variations. These methods will insert the rendered content of a template within the given layout. This is convenient for rendering HTML views inside a larger layout, amongst other use-cases.
The full list of corresponding methods is:
render_in_layout(template: string, data: any, layout: string, partials: any)
render_in_layout_file(template: string, data: any, layout_filename: string, partials: any)
render_from_filename_in_layout(filename: string, data: any, layout: string, partials: any)
render_from_filename_in_layout_file(filename: string, data: any, layout_filename: string, partials: any)
render_with_json_in_layout(template: string, json_filename: string, layout: string)
render_with_json_in_layout_file(template: string, json_filename: string, layout_filename: string)
render_from_filename_with_json_in_layout(filename: string, json_filename: string, layout: string)
render_from_filename_with_json_in_layout_file(filename: string, json_filename: string, layout_filename: string)
template := "Hello, {{name}}."
data := map[string]string{"name" = "Kilgarvan"}
layout := `Above >>
{{content}}
<< Below`
output, _ := render_in_layout(template, data, layout)
fmt.println(output)
// Above >>
// Hello, Kilgarvan.
// << Below
odin-mustache
works in two steps:
- Lexing and parsing
- Rendering with data
If you are rendering the same template multiple times (ex: sending out personalized emails to subscribers), the lexing+parsing step can be performed once to create a compiled template. This compiled template can then be used multiple times with different data.
src := "Hello, {{name}}!"
template: Template
output: string
data: map[string]string
partials: map[string]string
lexer := Lexer{src=src, delim=CORE_DEF}
lexer_parse(&lexer)
data = {"name" = "St. Charles"}
template = Template{lexer=lexer, data=data, partials=partials}
output, _ = template_render(&template)
// => Hello, St. Charles!
data = {"name" = "Edouard"}
template = Template{lexer=lexer, data=data, partials=partials}
output, _ = template_render(&template)
// => Hello, Edouard!
- Validate JSON keys
- Better CLI argument parsing
- Improve error handling and reporting
- Add optional logging for debugging and performance work
- Configurable precision for floating point types
- Add support for changing delimiters
- Special loop conditionals (eg., checking for the first iteration or last iteration)