Skip to main content

Filters & Functions

Every entry below is registered once and reachable in two equivalent surface forms:

{{ value | snake_case }} {# pipe form #}
{{ snake_case(value) }} {# function form #}

Pick whichever reads better. Zero-input generators (now(), uuid(), ...) are naturally used in function form. Filters take precedence over context keys of the same name, so setting a context variable named now cannot break {{ now() }}.

Case and inflection filters accept any scalar (string, integer, number, boolean — coerced to a string; nil becomes ""). Passing a table to a scalar filter is a render error that names the filter.

Casing Filters

All casing filters normalize from any input casing (snake, kebab, camel, Pascal, spaced, SCREAMING) before converting.

FilterInput → OutputExample
camel_caseany → camelCase{{ "FOO_BAR" | camel_case }}fooBar
pascal_caseany → PascalCase{{ "foo-bar" | pascal_case }}FooBar
snake_caseany → snake_case{{ "Foo Bar" | snake_case }}foo_bar
kebab_caseany → kebab-case{{ "FOO_BAR" | kebab_case }}foo-bar
constant_caseany → SCREAMING_SNAKE{{ "Foo bar" | constant_case }}FOO_BAR
train_caseany → Train-Case{{ "foo_bar" | train_case }}Foo-Bar
title_caseany → Title Case{{ "foo_bar" | title_case }}Foo Bar
sentence_caseany → Sentence case{{ "FooBar" | sentence_case }}Foo bar
class_caseany → singularized PascalCase{{ "foo_bars" | class_case }}FooBar
package_caseany → package.case{{ "FooBar" | package_case }}foo.bar
directory_caseany → directory/case{{ "FooBar" | directory_case }}foo/bar
cobol_caseany → COBOL-CASE{{ "foo_bar" | cobol_case }}FOO-BAR
upper_caseany → UPPERCASE (no word splitting){{ "foo-bar" | upper_case }}FOO-BAR
lower_caseany → lowercase (no word splitting){{ "Foo-Bar" | lower_case }}foo-bar
upperalias for upper_case{{ name | upper }}
loweralias for lower_case{{ name | lower }}

class_case differs from pascal_case in that it also singularizes: order_itemsOrderItem.

Inflection Filters

Backed by the archetect-inflections crate — English pluralization rules with irregular and uncountable word handling.

FilterInput → OutputExample
pluralizesingular → plural{{ "entity" | pluralize }}entities
pluralalias for pluralize{{ "ox" | plural }}oxen
singularizeplural → singular{{ "crates" | singularize }}crate
singularalias for singularize{{ "oxen" | singular }}ox
ordinalizenumber string → ordinal{{ "1" | ordinalize }}1st
deordinalizeordinal → number string{{ "12th" | deordinalize }}12

String Filters

FilterSignatureExample
defaultdefault(value, fallback) — fallback when value is nil or ""{{ nickname | default(name) }}
truncatetruncate(s, n, suffix?) — cut to n chars (Unicode-aware); appends suffix (default ) only if truncated{{ "abcdefghij" | truncate(5, "...") }}abcde...
replacereplace(s, from, to) — replace all occurrences{{ "banana" | replace("a", "o") }}bonono
trimstrip leading + trailing whitespace{{ " hi " | trim }}hi
trim_startstrip leading whitespace{{ " hi" | trim_start }}hi
trim_endstrip trailing whitespace{{ "hi " | trim_end }}hi
indentindent(s, n) — prefix every line with n spaces (empty trailing lines untouched){{ body | indent(4) }}
string_repeatstring_repeat(s, n) — repeat n times{{ "ab" | string_repeat(3) }}ababab
splitsplit(s, sep) — split into an array{% for part in split(path, "/") %}
lengthchar count for strings, element count for arrays, 0 for nil{{ "héllo" | length }}5
concatconcat(a, b, c, ...) — join scalar args{{ concat(org, ".", name) }}

string_repeat is deliberately not named repeat — that's a Lua reserved word, which would break the function form.

Collection Filters

Operate on array-style tables (the kind produced by split, YAML/JSON lists, and Lua sequences).

FilterBehaviorExample
joinjoin(arr, sep) — concatenate scalar elements{{ items | join(", ") }}a, b, c
firstfirst element, or nil if empty{{ items | first }}
lastlast element, or nil if empty{{ items | last }}
sortsorted copy (original untouched); scalar ordering{{ names | sort | join(", ") }}
reversereversed copy{{ items | reverse }}
containscontains(haystack, needle) — membership for arrays, substring for strings, false for nil haystack{% if contains(features, "TOC") %}
uniquededuplicated copy, first-occurrence order preserved{{ tags | unique | join(",") }}

contains is ATL's replacement for Jinja's in test: {% if "TOC" in features %} becomes {% if contains(features, "TOC") %}.

Date & Time Functions

FunctionReturnsExample result
now()current local datetime, RFC33392026-07-06T09:15:00-04:00
now_utc()current UTC datetime, RFC33392026-07-06T13:15:00+00:00
today()current local date2026-07-06
year()current local year (integer)2026
timestamp()current Unix timestamp (integer)1783430100
datedate(value, format) — strftime-format an RFC3339 or YYYY-MM-DD input{{ today() | date("%Y") }}2026
Copyright (c) {{ year() }} {{ org_name }}

UUID Functions

FunctionReturns
uuid()random v4 UUID (alias for uuid_v4())
uuid_v4()random v4 UUID
uuid_v7()time-ordered v7 UUID (sortable)
uuid_nil()00000000-0000-0000-0000-000000000000

Path Filters

Operate on POSIX-style forward-slash paths; purely string-based (never touch the filesystem).

FilterBehaviorExample
path_joinpath_join(a, b, ...) — join segments with /, collapsing duplicate separators; nil segments skipped{{ path_join("src", module, "mod.rs") }}src/api/mod.rs
basenamefinal path component{{ "a/b/c.txt" | basename }}c.txt
dirnameeverything before the final component{{ "a/b/c.txt" | dirname }}a/b
extnameextension including the dot ("" if none; leading dots as in .gitignore don't count){{ "a/b.tar.gz" | extname }}.gz
path_normalizecollapse . / .. segments and duplicate separators{{ "a/./b/../c" | path_normalize }}a/c

Serialization (the format global)

Not filters, but reachable from any expression via the read-only format global:

FunctionBehavior
format.to_json(value)Lua value → pretty JSON string
format.to_yaml(value)Lua value → YAML string
format.to_toml(value)Lua value → pretty TOML string
format.from_json(s)JSON string → Lua table
format.from_yaml(s)YAML string → Lua table
format.from_toml(s)TOML string → Lua table
{{ format.to_yaml(service_config) }}

format.json / format.yaml / format.toml are legacy aliases for the to_* forms.

Custom Filters

Archetype scripts can register their own filters with template.register_filters(table). Each entry becomes available in both pipe and function form, in file contents, file names, and inline template.render strings alike:

-- in the archetype script
template.register_filters({
rust_type = function(field)
local map = { String = "String", Integer = "i64", UUID = "Uuid", Boolean = "bool" }
return map[field.type] or field.type
end,
})
pub {{ field.name | snake_case }}: {{ field | rust_type }},

Custom filters receive the piped value as their first argument, followed by any filter arguments. Registering a name that already exists replaces it — including built-ins, so choose distinct names.