crimes against god and satan in declarative macros
a rustacean classic
mood: delightedcodingrustso, before realizing that glob
can do it, i wrote a tiny thing to do glob-matching. (tl;dr: it's how you specify which stylesheets to apply to which posts/pages in blog/reblog.)
naturally, i wrote unit tests for it. at first, they were pretty bulky – 6 lines for every glob and expression to test it against. before long i realized i could slim it a bit, but then it occurred to me i could write a macro to remove all the boilerplate.
so i did, and this is that macro:
macro_rules! tests {
( $(
$name:ident: $glob:literal
$( ! $(|$ne:literal|)?)? ~
$text:literal
);* $(;)? ) => {$(
#[test]
fn $name() {
assert!($(!$($ne)?)? Glob::new($glob).matches($text));
}
)*}
}
obviously (/s) this is crystal clear to any rust dev, but in case you want to see how it's used:
tests! {
match_exact: "foobar" ~ "foobar";
unmatch_suffix_exact: "foobar" !~ "asdf.foobar";
unmatch_prefix_exact: "foobar" !~ "foobar.asdf";
match_star_text: "*" ~ "asdf.fdsa";
match_star_empty: "*" ~ "";
match_suffix: "*foobar" ~ "asdf.foobar";
unmatch_suffix: "*foobar" !~ "asdf.foobar.fdsa";
match_prefix: "foobar*" ~ "foobar.fdsa";
unmatch_prefix: "foobar*" !~ "asdf.foobar.fdsa";
match_middle: "foo*bar" ~ "foo.meow.bar";
unmatch_middle_prefix: "foo*bar" !~ "asdf.foo.meow.bar";
unmatch_middle_suffix: "foo*bar" !~ "foo.meow.bar.fdsa";
match_multi: "foo*meow*bar" ~ "foo____meow____bar";
unmatch_multi_prefix: "foo*meow*bar" !~ "asdf.foo____meow____bar";
unmatch_multi_suffix: "foo*meow*bar" !~ "foo____meow____bar.fdsa";
match_leading_multi: "*meow*bar" ~ "foo____meow____bar";
unmatch_leading_multi_prefix: "*meow*bar" ~ "asdf.foo____meow____bar";
unmatch_leading_multi_suffix: "*meow*bar" !~ "foo____meow____bar.fdsa";
match_trailing_multi: "foo*meow*" ~ "foo____meow____bar";
unmatch_trailing_multi_prefix: "foo*meow*" !~ "asdf.foo____meow____bar";
unmatch_trailing_multi_suffix: "foo*meow*" ~ "foo____meow____bar.fdsa";
}
and then, after writing all this and using it, i realized that glob
can do everything i need and more. so that code's gone now! but it's still an archetypical example of the kind of thing decl macros are perfect for: tiny, quick dsls to automate the boilerplate away.
someday i'll write up an explainer with all my random tips and tricks about writing these. they're extremely expressive and powerful, and for a lot of things, way easier than proc macros, even with syn and co. for now, though, i just thought y'all would enjoy this minor crime.
with luck, i'll have more to write about b/rb proper soon. for now i have other obligations to tend to. in the mean time, expect more rechosts, or maybe a roundup – i'm due for one.