cookie_cutter

Crates.iocookie_cutter
lib.rscookie_cutter
version0.2.0
created_at2025-09-11 16:19:36.061135+00
updated_at2025-09-20 14:52:46.021268+00
descriptionA feature-rich template engine with context aware escaping and both runtime and compiletime compilation
homepage
repositoryhttps://github.com/Cookie04DE/cookie_cutter/
max_upload_size
id1834028
size90,397
(Cookie04DE)

documentation

README

crates.io crates.io License actions-badge

Cookie Cutter

Introduction

A feature-rich template engine with context aware escaping and both run time and compile time compilation.

Designed to generate html / js / css documents based on trusted templates but using untrusted user data that are safe to be processed by a browser.

Unlike other template engines cookie cutter does not aim to be minimal or maximally performant instead opting for feature richness and developer experience.

It also does not require templates to be compiled into the application, enabling them to be loaded at run time from a filesystem for example. Compile time inclusion is however also possible and has the benefit of surfacing template errors directly at compile time as native rust compiler errors to catch mistakes as early as possible.

Minimal "Hello World" example

hello.cctmpl

template hello() {
    Hello World!
}

hello.rs

use std::collections::{BTreeMap, HashMap};

use cookie_cutter::{
    builtins::{StandardContextWorker, StandardContextualizer, StandardEscaper},
    Templates, Value,
};

fn main() {
    let t: Templates<_> =
        Templates::<StandardEscaper>::new::<StandardContextualizer, StandardContextWorker>(
            [(
                "hello.ccmtmpl",
                include_str!("hello.cctmpl"),
            )],
            HashMap::new(),
        )
        .expect("failed to compile templates");

    assert_eq!(
        t.render("hello", Value::Struct(BTreeMap::new()))
            .expect("failed to render hello template"),
        "Hello World!".to_string()
    );
}

Further examples (also showcasing usage in an http server for example) can be found in the examples directory.

The testdata/source_files directory might also be of interest to you if want to see what the language is capable of.

Feature Overview

Type system

The template definition language is statically typed. It's type system allows for text types as well as booleans, integers and floats. Struct and tuple like datatypes build on top of these primitive types and allow for complex data to be represented and used by the templates. The data type can also be omitted and in that case defaults to a specific text type (usually text). This is of further importance; for that see Contextualization.

It also permits recursive types as well sum types (like Rust enums).

Values

Values can be created programmatically (by building up a Value enum) or via the value! macro allowing for JSON-like value literals.

Cookie cutter also has support for serde to serialize Rust values directly to values for use as template arguments.

Indentation control

The templating language is indentation aware and automatically strips the longest common whitespace prefix of each line in a template / template literal.

If required this can be manually adjusted using a so called indentation marker which sets the minimum indentation level to a user defined level.

Template literals (comparable to partials in other template engines)

Template literals allow you to write pieces of text in a specific context (text type) using local variables and pass them to other templates as arguments for example. This can be used to define a common site template which contains all the html boilerplate as well as other common elements like footers and headers and take the actual content of the page as a parameter to be included as the body.

Comments

Template authors can included comments both in the source files directly (outside of any templates) using familiar C-style comments or inside templates using commands.

Commands

Static and dynamic content is differentiated by use of commands. Everything outside of commands is literal (static) and everything inside gets computed at render time.

Commands can be expressions (to included dynamic content in templates), variable assignments or just comments.

Commands are started by a variable number of open curly braces to comfortably accommodate static content which commonly requires curly braces and avoids the constant use of escape sequences. The curly count is determined at the start of the template body.

Escape sequences

Character sequences with a special meaning can be used literally (escaped) by being prefixed with a backslash.

Newlines can be escaped similar to multi line shell commands to write static content across multiple lines without the result having multiple lines as well.

Variables

The language supports variable which need not be declared but are statically typed (like the rest of the langauge) and need to be assigned before usage.

These store arbitray values and can be reassigned.

Direct array value inclusion a.k.a. freestanding expressions

Array values can also be directly included in a templates content (given that the two text types are compatible) and the array's values will be included with the correct indentation and newline in the templates body.

Control flow

The language supports if and each expressions.

The if expression allows values to be selected dynamically, for example to change a login button to logout button if the user is already logged in.

The each expression allows you to map over an array and produces a new array. This is commonly used to include a list of things in a templates output. Direct array value inclusion allows you to include this mapped output directly into your templates with the correct indentation and newlines.

Functions

There are a few builtin functions available to template authors to suit common use cases. These are subject to be extended based on user feedback.

The function call syntax is reminiscent of Haskell and since pipelines of function calls are pretty common (where a function get's called with the result of another function and so on) cookie cutter includes the dollar sign syntax to cut down on parentheses which Haskell users should find familiar.

Custom functions can be written to extend the capabilities of the template engine at render time. These take precendence over the builtin ones and allow you to alter their behaviour if needed.

Contextualization

The text type is context aware and automatically ensures correct escaping based on the source and destination type. You can safely include a text piece into html or js.string.s (a single quote JS-String) and cookie cutter handles the escaping automatically.

Although web content generation is the primary focus of the project other types of text content can also be supported. For this you can implement your own Contextualizer instead of utizilizing the standard one. This provides custom text types and context detecting. You can also opt out of this feature entirely by using the NoopContextualizer which only supports the text type and does no context analysis.

Output

Template output can be obtained as a String or be directly written to a fmt::Write implementor.

Commit count: 138

cargo fmt