Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Code Templates

CodeTemplate provides named parameters on top of CodeBlock’s positional format strings. Templates are language-agnostic: you define the pattern once, then apply it with concrete arguments for any target language.

Syntax

Templates use #{name:K} for named parameters, where K specifies the kind:

KindSpecifierArgument Type
T%TTypeName
N%NNameArg
S%SStringLitArg
L%L&str, String, or CodeBlock

Use ## to emit a literal # character.

Bare positional specifiers (%T, %N, etc.) are rejected in templates. You must use the named #{...} syntax.

Basic Usage

use sigil_stitch::code_template::CodeTemplate;
use sigil_stitch::code_block::NameArg;
use sigil_stitch::lang::typescript::TypeScript;
use sigil_stitch::type_name::TypeName;

let tmpl = CodeTemplate::new("const #{var:N}: #{type:T} = #{init:L}").unwrap();

let block = tmpl.apply()
    .set("var", NameArg("user".into()))
    .set("type", TypeName::primitive("string"))
    .set("init", "null")
    .build()
    .unwrap();
// Output: const user: string = null

The template is parsed once by CodeTemplate::new(). Arguments are supplied via .apply().set(...).build(), producing a language-agnostic CodeBlock.

Reuse Across Types

The same template works for different types and values:

let field_tmpl = CodeTemplate::new("#{name:N}: #{type:T}").unwrap();

// Apply for a string field
let string_field = field_tmpl.apply()
    .set("name", NameArg("username".into()))
    .set("type", TypeName::primitive("string"))
    .build()
    .unwrap();

// Apply for a number field
let number_field = field_tmpl.apply()
    .set("name", NameArg("age".into()))
    .set("type", TypeName::primitive("number"))
    .build()
    .unwrap();

Reuse Across Languages

Since templates are language-agnostic, the same template can target different languages:

use sigil_stitch::lang::rust_lang::RustLang;

let decl = CodeTemplate::new("#{name:N}: #{type:T} = #{value:L}").unwrap();

// TypeScript
let ts_block = decl.apply()
    .set("name", NameArg("count".into()))
    .set("type", TypeName::primitive("number"))
    .set("value", "0")
    .build()
    .unwrap();

// Rust
let rs_block = decl.apply()
    .set("name", NameArg("count".into()))
    .set("type", TypeName::primitive("i32"))
    .set("value", "0")
    .build()
    .unwrap();

Duplicate Parameters

The same parameter name can appear multiple times. The value you set is used at each occurrence:

let tmpl = CodeTemplate::new("#{type:T} -> #{type:T}").unwrap();

let block = tmpl.apply()
    .set("type", TypeName::primitive("string"))
    .build()
    .unwrap();
// Output: string -> string

Import Tracking

Templates using #{name:T} track imports just like %T in CodeBlocks. When the resulting CodeBlock is rendered inside a FileSpec, all type references are collected for the import header:

let tmpl = CodeTemplate::new("const #{var:N}: #{type:T} = new #{type:T}()").unwrap();
let user = TypeName::importable_type("./models", "User");

let block = tmpl.apply()
    .set("var", NameArg("user".into()))
    .set("type", user)
    .build()
    .unwrap();
// When rendered: import type { User } from './models'
// Output:        const user: User = new User()

Validation

.build() validates that:

  • All parameters have been set (missing parameters produce an error)
  • Argument kinds match the parameter kind (#{name:T} must receive a TypeName, not a string)
let tmpl = CodeTemplate::new("#{name:N}: #{type:T}").unwrap();

// Missing parameter
let result = tmpl.apply()
    .set("name", NameArg("x".into()))
    // forgot to set "type"
    .build();
assert!(result.is_err());

Introspection

Use param_names() to inspect a template’s parameters:

let tmpl = CodeTemplate::new("#{name:N}: #{type:T} = #{init:L}").unwrap();
let params = tmpl.param_names();
// [("name", ParamKind::Name), ("type", ParamKind::Type), ("init", ParamKind::Literal)]

When to Use Templates vs CodeBlock

  • CodeBlock: When you’re building code imperatively and the structure varies at runtime.
  • CodeTemplate: When you have a fixed pattern that gets reused with different values. Templates make the pattern explicit and prevent positional argument errors.
  • sigil_quote!: When you can write the target code inline at compile time.