Build Integration
Installation
Install the CLI compiler:
curl -fsSL https://waltzing.awesomike.com/install | bashBuild Script
Create a build.rs file to compile templates during build:
Basic Setup (Buffered Mode)
use std::process::Command;
fn main() {
let out_dir = std::env::var("OUT_DIR").unwrap();
let status = Command::new("waltzing")
.args(["-i", "templates", "-o", &out_dir])
.status()
.expect("Failed to run waltzing compiler");
if !status.success() {
panic!("Waltzing compilation failed");
}
println!("cargo:rerun-if-changed=templates");
} Streaming Mode
let status = Command::new("waltzing")
.args(["-i", "templates", "-o", &out_dir, "--streaming"])
.status()
.expect("Failed to run waltzing compiler"); Async Mode
let status = Command::new("waltzing")
.args(["-i", "templates", "-o", &out_dir, "--async"])
.status()
.expect("Failed to run waltzing compiler");Output Modes
Waltzing supports three output modes that affect how generated template functions work:
| Mode | Flag | Function Signature | Best For |
|---|---|---|---|
| Buffered | (default) | fn apply(...) -> Content | Simple usage, returns complete HTML |
| Streaming | --streaming | fn apply(buf: &mut String, ...) | Memory efficiency, incremental output |
| Async | --async | async fn apply(w: &mut W, ...) -> io::Result<()> | Async runtimes, streaming responses |
Using Generated Templates
Buffered Mode (Default)
Templates return a Content type that can be converted to a String:
mod templates {
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
}
fn render_page(title: &str, posts: Vec) -> String {
templates::pages::home::apply(title, posts).to_string()
} Streaming Mode
Templates write directly to a buffer. Use the buffered! macro to collect output:
mod templates {
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
}
fn render_page(title: &str, posts: Vec) -> String {
// Access buffered! macro
templates::rt::buffered!(templates::pages::home::apply(title, posts))
} Async Mode
Templates are async and write to an AsyncWrite. The buffered! macro returns a future:
mod templates {
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
}
async fn render_page(title: &str, posts: Vec) -> std::io::Result {
templates::rt::buffered!(templates::pages::home::apply(title, posts)).await
} The buffered! Macro
In streaming and async modes, the buffered! macro collects template output into a String instead of writing to a provided buffer.
Syntax
// Without arguments
templates::rt::buffered!(templates::component::apply())
// With arguments
templates::rt::buffered!(templates::pages::home::apply(title, posts)) Why rt?
The macro is namespaced under rt (runtime) to avoid conflicts when using multiple template directories compiled with different output modes.
CLI Options
| Option | Description |
|---|---|
-i, --input | Input template directory (can specify multiple, supports alias=path syntax) |
-o, --output | Output directory for generated Rust code |
--streaming | Generate streaming functions |
--async | Generate async streaming functions |
--with-axum | Enable axum::IntoResponse impl |
--with-uuid | Enable From<Uuid> impl |
--preserve-whitespace | Keep original whitespace (default: minify) |
--debug-comments | Emit HTML comments at component boundaries |
With Axum
When compiled with --with-axum, Content implements IntoResponse:
use axum::{routing::get, Router};
mod templates {
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
}
async fn home() -> impl axum::response::IntoResponse {
templates::pages::home::apply("Welcome")
}
fn main() {
let app = Router::new().route("/", get(home));
// ...
}