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.
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 std::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));
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(),
})
}
}
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]);
i
variable in both contexts. Uppercase identifiers indicate to metamatch that you want these names
to be replaced inside of nested quote!
blocks.
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. See the documentation of
quote!
for
a full list of template block tags.
let
statements and basic pattern matching.loop
,while
,while let
, andfor
loops, includingcontinue
andbreak
if
andelse
blocks- Functions (
fn
) and lambdas (|..|
) - Arrays (
[1,2,3]
) - Ranges (
0..=10
) - All basic operators:
+
,-
,*
,/
,%
,<<
,>>
,=
,+=
,-=
,*=
,/=
,%=
,<<=
,>>=
,&=
,|=
,^=
,&
,|
,^
,!
,<
,>
,<=
,>=
,==
,!=
,&&
,||
,[..]
struct
,enum
,match
,type
,trait
, ...
lowercase(str) -> str
uppercase(str) -> str
capitalize(str) -> str
enumerate([T]) -> [(int, T)]
zip([A], [B], ..) -> [(A, B, ..)]
map([T], Fn(T) -> U) -> [U]
chars(str) -> [char]
bytes(str) -> [int]
ident(str) -> token
str(any) -> str
len([T]) -> int
All functions support UFCS, so [1,2,3].len() == len([1,2,3])
String functions also work on tokens.
quote! {..} -> [token]
: nested version of [quote!
].raw! {..} -> [token]
: raw Rust, no template tags or expanded meta variables
Just like Rust macros, you can use any of {}
, []
, and ()
interchangably for the macro invocations.
MIT or Apache Version 2.0, at your option.