Introduction

Templar is a templating tool for the command line or available as a library for Rust. Templar attempts to be a lightweight tool that runs with very few dependencies, distributed in binary form for a number of architectures.

Installation

We try to supply easy install methods for different platforms on a best-effort basis.

Binary Install

The primary way Templar is distributed is in binary form from Github. Binaries are available for a number of platforms and can be quickly installed via script. A simple script install can be like this:

# Make sure to pick the architecture and variant for your platform
curl -sL https://github.com/proctorlabs/templar/releases/download/v0.3.0/templar-x86_64-unknown-linux-gnu.tar.xz |
    tar -xJ -C /usr/local/bin && chmod +x /usr/local/bin/templar

Cargo (source)

If Cargo is available, templar can be quickly installed with this command:

cargo install --all-features templar

Templar CLI

The CLI can be used to run expressions or execute templates to STDOUT or an output file.

Usage

Tu process a template, templar template <args>

templar-template 0.3.0
Execute a template and render the output

USAGE:
    templar template <file>

OPTIONS:
    -d, --dynamic <dynamic>...    File to parse and load into the templating context as a dynamic input
    -h, --help                    Prints help information
    -i, --input <input>...        File to parse and load into the templating context
    -o, --output <output>         Output to send the result to, defaults to stdout
    -s, --set <set>...            Directly set a variable on the context

ARGS:
    <file>    Template file(s) to open

To run an expression directly, templar expression <args>

templar-expression 0.3.0
Execute an expression and render the output

USAGE:
    templar expression <text>

OPTIONS:
    -d, --dynamic <dynamic>...    File to parse and load into the templating context as a dynamic input
    -h, --help                    Prints help information
    -i, --input <input>...        File to parse and load into the templating context
    -o, --output <output>         Output to send the result to, defaults to stdout
    -s, --set <set>...            Directly set a variable on the context

ARGS:
    <text>    The expression to run

Templating

Templar's templating syntax is inspired by Jinja2 and Ansible.

Examples

The templating syntax is likely familiar considering the frameworks that it is based on. For instance, a simple template may look like this:

user_name={{ user.name }} {# Replace with the context property 'name' in 'user' #}
full_context={{ . | json("pretty") }} {# Dump the entire context as JSON, '.' is the root node #}
password={{ script('echo hunter2 | md5sum') }} {# Execute a shell command and calculate the MD5 sum #}

In addition to simple replacements, more complex expressions can be used.

The calculated result is {{ 100 * 5 / 10 }} {#- Prints '50' #}

Today's guest list:
{%- for person in ['Bob', 'Joe', 'Jen', 'Amy')] %}
* {{ person }} will come to the party!
{%- endfor %} {#- This will loop everyone in the inline array above, but they array could also come from the context #}

Syntax

Much of the syntax is based on the wonderful Jinja2 project. Here are some of the currently supported features.

  • Value replacement can be done using the {{ }} syntax.
    • Literals supported are strings (single, double, or backtick quoted), boolean, numbers (currently parsed as i64), null, arrays, and maps
    • Identifiers that start with an alphabetic character can be referred to directly e.g. {{ some.value.path }}
    • The root node can be referred to with . allowing things like {{ . | json }} to be used to dump the entire context as JSON
    • Identifiers of non-standard type, e.g. starting with a non-alphabetic character, spaces, etc. can be referred to using the bracket syntax. e.g. {{ .['565'] }}. This also allows array access and identifier of non-standard types (such as boolean).
    • Inline arrays: {{ [1,2,3,4] }} and complex nesting also possible e.g. {{ [1,2, script("echo 'hello world!'"), (5 + 5 | base64)] }}
    • Inline maps: {{ {'key': 'value', 'otherKey': { 'nested': 'map' } } }}
  • Control flow can be done using the {% %} syntax
    • If/else if: {% if 10/2 == 5 %}The world is sane!{% else if false %}What universe are we in?{% end if %}
    • Scoping can be done manually: {% scope %}I'm in a scope!{% end scope %}
    • For loops: {% for thing in lots.of.stuff %} {{ thing['name'] }} {% end for %}. For loops always enter a new scope.
  • Comments use the {# #} syntax and will be remitted from the output.
  • Whitespace control can be accomplished by adding a - to any of the above blocks e.g. {{- 'no whitespace! -}}.
    • Whitespace control can be added to one or both sides of the tags. All spaces, new lines, or other whitespace on the side with the - on it will be removed as if the block is immediately next to the other element.

As documentation is still in progress, see the kitchen sink for examples of template usage.

Expressions

Everything inside the standard {{ }} block is an expression. Each block holds exactly one expression, but that expression can be chained with many individual operations. A quick overview:

  • Math operations: + - * / % these operations are only valid with numeric types
  • Equality: == != < <= > >= && ||
  • Value setting: = the left side of this operation must be some identifier e.g. {{ some.val.path = 'hello world!' }}
  • String concatenation: ~ e.g. {{ 'Hello' ~ ' ' ~ 'world!' }} prints "Hello world!"
  • Functions: ident() e.g. {{ env('USER') }} would retrieve the value of the environment variable "USER".
  • Filters: | e.g. {{ 'hello world' | upper }} would use the 'upper' filter to print "HELLO WORLD"

As documentation is still in progress, see the expression tests for examples of expression usage.

Filters

Filters are used to process the result of an expression in a template.

Overview

As an example, the expression {{ 'hello' | upper }} uses the "upper" filter to create the upper case result "HELLO".

Built in filters

  • require: Will throw an error if the result is empty or null
  • default(any): Replaces empty, null, or error types with the default value from the args
  • length: Returns the length of a string or array
  • lower: Lowercase the rendered result
  • upper: Uppercase the rendered result
  • trim: Trim whitespace off the rendered result
  • split(str?): Split a string into an array. Delimited by newline, but an arg can be used to override the delimiter.
  • index(int): Retrieve the int index from the array
  • join(str?): Join an array with the provided string. Defaults to newline
  • string: Forces the result into a string type, usually by rendering it
  • key(str): Retrieve the value of the specified key from the dictionary
  • escape_html: (alias 'e') Render the result and escape HTML characters
  • yaml: (alias yml) Serialize the data into a YAML string. Requires"yaml-extension" feature (default on)
  • json(str?): Serialize the data into a JSON string. Set str to 'pretty' to print with indentation. Requires the "json-extension" feature (default on)
  • base64(str?): Encode the result as Base64. If the optional string parameter is set to "decode" then it will try to decode instead. Requires "base64-extension" feature (default on)

Functions

Functions are used for pulling or creating data from other sources or using other methods.

Overview

As an example, the file() function will open a file and return the contents as a string. Functions do not require any data to work, though most need arguments. Functions can be used as an argument to other functions, to filters, or as the base operation. For example, this is a valid expression:

{ 'filename': 'settings.json', 'content': json(file('settings.json')) } | yml

The above will creat a map with two fields "filename" and "content" with content containing the parsed contents the file settings.json. Then we pass this map to the filter yml to then render that map into a serialized YML string.

Built in functions

  • file(str): Open file and read contents to a string
  • env(str): Read the named environment variable
  • script(str): Execute the string as a shell script. Returns a map with keys "stdout", "stderr", "status"
  • command(str, str[]?): Execute the supplied command with the supplied arguments. Returns a map with keys "stdout", "stderr", "status"
  • json(str): Parse the supplied JSON string into a map. Requires "json-extension" feature (default on)
  • yaml(str): (alias yml) Parse the supplied YML string into a map. Requires "yaml-extension" feature (default on)

API Documentation

Complete documentation for the development API is available at docs.rs.