Expand description
macroweave provides procedural macros for generating repeated Rust code from compact, table-driven inputs.
§Motivation
macroweave is for repetition that has to become Rust syntax, not runtime control flow. You write the choices once, name the columns, and use those names in Rust syntax.
That is the table-driven case macroweave is built around:
use macroweave::repeat;
trait ReadLe {
fn read_le(input: &[u8]) -> Self;
}
repeat!((Ty, Width) in [
(u16, 2),
(u32, 4),
(u64, 8),
] {
impl ReadLe for Ty {
fn read_le(input: &[u8]) -> Self {
Ty::from_le_bytes(input[..Width].try_into().unwrap())
}
}
});
assert_eq!(u16::read_le(&[0x34, 0x12]), 0x1234);
assert_eq!(u32::read_le(&[1, 0, 0, 0]), 1);This cannot be written as an ordinary for-loop because Ty and Width need to be substituted as tokens before the generated code is type-checked.
§Whole-body repetition
Without splice syntax, repeat! emits the whole body once per input row:
use macroweave::repeat;
trait TypeName {
const NAME: &'static str;
}
repeat!((Ty, Name) in [
(u8, "u8"),
(u16, "u16"),
(u32, "u32"),
] {
impl TypeName for Ty {
const NAME: &'static str = Name;
}
});
assert_eq!(<u8 as TypeName>::NAME, "u8");
assert_eq!(<u16 as TypeName>::NAME, "u16");
assert_eq!(<u32 as TypeName>::NAME, "u32");§Partial repetition
When only part of a surrounding construct should repeat, use splice! and put that part in #( ... )*. A single separator can be written before *, such as #( ... ),* for comma-separated output:
use macroweave::splice;
fn keyword_code(text: &str) -> Option<u8> {
splice!((Pat, Code) in [
("async", 1u8),
("await", 2u8),
] {
match text {
#(Pat => Some(Code)),*,
_ => None,
}
})
}
assert_eq!(keyword_code("async"), Some(1));
assert_eq!(keyword_code("await"), Some(2));
assert_eq!(keyword_code("fn"), None);Placeholders 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 placeholder. 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!.
§Syntax notes
- Bind placeholders as bare identifiers, such as
TyorName. - Tuple rows bind multiple placeholders, and
_skips a row value. - Row values can contain one or more Rust tokens. Top-level commas separate rows.
- Nested invocations are supported. Use different placeholder names at each level.