sailfish
A simple, small, and extremely fast compiled template engine for Rust, inspired by EJS syntax.
Repository Health
Technical Analysis
Sailfish is a Rust template engine that compiles .stpl template files into native Rust code at build time via a derive macro, rather than interpreting templates at runtime. Templates use an EJS-inspired syntax (<% %> for statements, <%= %> for escaped output, <%- %> for raw output) and can directly reference struct fields and call methods on self, giving full compile-time type checking of template expressions.
The project is organized as a workspace of focused crates: sailfish (the public API and runtime, including a SIMD-accelerated HTML escaper with AVX2/SSE2 code paths and a scalar fallback), sailfish-compiler (parser, analyzer, optimizer, and code generator shared by the proc-macro crates), and sailfish-macros (the #[derive(Template)], TemplateOnce, TemplateMut, and TemplateSimple proc-macros). This split keeps the runtime dependency surface minimal — fewer than 15 crates in total — while the heavier compiler logic only runs at build time.
What You Get
- Four derive macros (
Template,TemplateOnce,TemplateMut,TemplateSimple) covering by-reference, consuming, mutable, and self-less rendering styles - A
runtimemodule exposingBuffer, theRendertrait for custom types, and aSizeHintmechanism that pre-allocates output buffers based on previously observed render sizes - SIMD-accelerated HTML escaping with dedicated AVX2 and SSE2 code paths plus a portable fallback, selected automatically at compile/runtime
- Built-in filters (including an optional
jsonfilter behind a feature flag) and support for including one template from another - Optional
sailfish.tomlconfiguration file support (via theconfigfeature) for customizing template directories and escaping behavior - Compile-time error messages that point at the offending line in the original
.stplfile, not the generated Rust code
Common Use Cases
- Server-rendered HTML views in Rust web frameworks such as Actix Web, where response bodies are built from struct data with zero runtime template parsing
- High-throughput services where template rendering is on the hot path and interpreted engines (Handlebars, Tera) introduce measurable overhead
- Generating static site output or documentation pages ahead of time, using
TemplateOncefor one-shot rendering - Email or report generation where structured data needs to be turned into HTML with predictable, type-checked output
- Projects that want compile-time guarantees that referenced fields and methods actually exist on the template’s context struct
Under The Hood
Architecture
The workspace separates concerns cleanly across four crates: sailfish (public traits Template/TemplateOnce/TemplateMut/TemplateSimple in sailfish/src/lib.rs plus the runtime module), sailfish-compiler (the actual template pipeline: parser.rs tokenizes .stpl syntax, analyzer.rs and optimizer.rs transform the parsed tree, translator.rs emits Rust source, and procmacro.rs wires this into the four derive entry points), sailfish-macros (a thin proc_macro_derive shim that forwards into sailfish-compiler::procmacro), and sailfish-tests/integration-tests (a dedicated workspace member for end-to-end and compile-fail testing). Because template compilation happens entirely inside sailfish-compiler at macro-expansion time, the runtime crate (sailfish) stays free of parsing/codegen dependencies, and changing the compiler internals cannot affect the rendering hot path directly — only the code it generates.
Tech Stack
The core sailfish crate targets Rust edition 2024 (minimum supported Rust version 1.89) and keeps runtime dependencies minimal: itoap and ryu for fast integer/float formatting, with serde/serde_json gated behind an optional json feature. sailfish-macros builds on proc-macro2, syn, and quote, standard tooling for Rust procedural macros, and forwards derive logic into sailfish-compiler. A build-dependencies entry on version_check lets the crate adapt codegen to the detected rustc version. The project ships Cargo feature flags (config, derive, json, perf-inline, hermetic) to let consumers opt in or out of proc-macro and configuration-file support.
Code Quality
The project has a dedicated sailfish-tests/integration-tests workspace crate with both positive tests (template.rs, template_simple.rs, config.rs, compile.rs) and a tests/fails/ directory of trybuild-style compile-fail fixtures with .stderr golden files, verifying that malformed templates (unclosed delimiters, missing semicolons, unbalanced braces, unknown options) produce the intended compiler diagnostics. Error handling is explicit and typed: sailfish-compiler/src/error.rs defines a non-exhaustive ErrorKind enum with From conversions from fmt::Error, io::Error, and syn::Error, and an Error struct that tracks source file, offending source text, and byte offset for precise diagnostics. CI (.github/workflows/test.yml) runs the test suite across Linux, macOS, and Windows on stable, beta, and nightly toolchains plus a 32-bit target, and a separate coverage.yml workflow tracks code coverage — indicating a mature, cross-platform-tested codebase.
What Makes It Unique
Unlike interpreted Rust template engines, Sailfish compiles templates to native Rust code ahead of time via proc-macros, so template logic gets full compiler type-checking and there is no runtime template-parsing cost — this is the basis for its consistently strong results in the community template-benchmarks-rs suite. It further differentiates itself with hand-written SIMD HTML-escaping routines (separate AVX2 and SSE2 implementations with automatic fallback), a level of low-level performance engineering uncommon among templating libraries in any language.