Crates.io | metamatch |
lib.rs | metamatch |
version | 0.3.0 |
created_at | 2024-06-03 02:29:44.398619+00 |
updated_at | 2025-04-10 01:30:28.612434+00 |
description | A proc-macro for generating repetitive match arms. |
homepage | |
repository | https://github.com/cmrschwarz/metamatch |
max_upload_size | |
id | 1259624 |
size | 223,458 |
metamatch!
A zero dependency proc-macro for practical metaprogramming.
Macro expressions using familiar Rust syntax, evaluated by a tiny interpreter.
Two specialized entrypoints for common usecases, two generalized versions for maximum flexibility.
#[replicate]
Generate repetitive syntax like trait impls without giving up on
rustfmt
or rust-analyzer.
use metamatch::replicate;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct NodeIdx(usize);
#[replicate(
let traits = [Add, Sub, Mul, Div, Rem];
for (TRAIT, FUNC) in zip(traits, traits.map(lowercase))
)]
impl core::ops::TRAIT for NodeIdx {
type Output = Self;
fn FUNC(self, rhs: Self) -> Self {
NodeIdx(self.0.FUNC(rhs.0))
}
}
assert!(NodeIdx(1) + NodeIdx(2) == NodeIdx(3));
metamatch!
The original motivation and namesake of this crate.
Match arms for differently typed variants cannot be combined, even if the are
syntactically identical. This macro lets you stamp out the neccessary
copies using (#[expand]
).
Just like #[replicate]
, this macro is fully compatible rustfmt
and rust-analyzer. It will be correctly formatted like a regular
match
expression, and is targettable even by auto refactorings
that affect the #[expand]
, like changing the name of an enum variant.
use metamatch::metamatch;
enum DynVec {
I8(Vec<i8>),
I16(Vec<i16>),
I32(Vec<i32>),
I64(Vec<i64>),
}
impl DynVec{
fn len(&self) -> usize {
metamatch!(match self {
#[expand(for X in [I8, I16, I32, I64])]
DynVec::X(v) => v.len(),
})
}
// v expands into v
fn len_(&self) -> usize {
match self {
DynVec::I8(v) => v.len(),
DynVec::I16(v) => v.len(),
DynVec::I32(v) => v.len(),
DynVec::I64(v) => v.len(),
}
}
}
unquote!
Evaluate arbitrary expressions.
use metamatch::unquote;
const ARRAY: [i32; 4] = unquote! {
let ELEMENTS = for X in 1..5 {
quote!(X,)
};
quote!([ELEMENTS])
};
assert_eq!(ARRAY, [1, 2, 3, 4]);
Note: By default, metamatch will not replace identifiers
in your quoted sections that happen to also be defined in your metaprogram.
Because there's no shadowing in the templates (they're just tokens),
this could lead to really subtle bugs e.g. if you used an i
variable in both contexts. Uppercase identifiers indicate to metamatch that you want these names
to be replaced inside of nested quote!
blocks.
quote!
Like unquote!
, but starts out in quoted mode.
It supports [< ... >]
styled template tags for readable templating
within large blocks of rust code with few dynamic parts.
use metamatch::quote;
quote! {
enum ErrorCode {
[<for err_id in 0..=42>]
[<ident("E" + str(err_id))>](String),
[</for>]
}
};
let err = ErrorCode::E42("oh noes!".to_owned());
You can switch between quoted and unquoted mode from within any macro using
the [<quote>]
and [<unquote>]
template tags.
The quote!(..)
used earlier is a covenience alias for [<quote>]..[</quote>]
MIT or Apache Version 2.0, at your option.