macro-template
macro-template provides template!, a procedural macro for generating repeated Rust code from compact, table-driven inputs.
Motivation
macro-template resulted from a ScopeDB code refactor. ScopeDB has used match-template for variant/type match arms and macro_find_and_replace for repeating Rust fragments over type lists. While replacing them, I found that I wanted the same thing in both places: write the choices once, name the columns, and use those names in Rust syntax. There was no existing macro fitting that shape.
That is the table-driven case template! is built around:
template!
assert_eq!;
assert_eq!;
When looking for existing approaches, I also found seq-macro, which covers a neighboring repetition pattern: range-driven generation, where N in 0..=2 becomes literal tokens like 0, 1, and 2. template! keeps both forms under one syntax: table rows, ranges, #( ... )* for partial repetition, and multiple for clauses for Cartesian products. The examples below expand each case.
Examples
Whole-body repetition
Without splice syntax, the whole template body is repeated once per input row:
template!
assert_eq!;
Partial repetition
When only part of a surrounding construct should repeat, put that part in #( ... )*. A single separator token tree can be written before *, such as #( ... ),* for comma-separated output:
assert_eq!;
assert_eq!;
assert_eq!;
When a template contains #( ... )* or #( ... ),*, template variables are substituted only inside the splice body, and the surrounding tokens are emitted once. Surrounding identifiers stay literal, even when they have the same name as a template variable. If a value should vary, place it in the splice body.
#( ..., )* and #( ... ),* are different: the latter does not produce a trailing comma. This matches delimiter repetition in macro_rules!.
Range inputs
Inputs can also be ranges of integers, characters, or bytes. Range inputs are written directly after in, without surrounding brackets. Wrap the range in parentheses when calling a range method such as .rev():
let tuple = ;
let mut fields = vec!;
template!
assert_eq!;
This cannot be written using an ordinary for-loop because elements of a tuple can only be accessed by their integer literal index, not by a variable.
Integer ranges preserve the radix, suffix, and shared padding width from their bounds. .strip_prefix() removes the radix prefix before substitution, which is useful when combining range values with paste for identifier generation:
template!
let _ = ;
Cartesian products
Multiple input clauses form a Cartesian product in clause order. This is useful when two or more independent dimensions share the same generated body:
;
;
template!
assert_eq!;
Minimum Rust version policy
This crate's minimum supported rustc version is 1.85.0.
The current policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if crate 1.0 requires Rust 1.85.0, then crate 1.0.z for all values of z will also require Rust 1.85.0 or newer. However, crate 1.y for y > 0 may require a newer minimum version of Rust.
License
This project is licensed under Apache License, Version 2.0.