For the the command command in Redis we want to display a list of all the available commands.
Doing so by hand is prone to forgetting to include a newly implemented command and have the list become outdated.
The following proc-macro adds a method to an enum
which lists all its variants by deriving VariantName
.
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput};
#[proc_macro_derive(VariantNames)]
pub fn enum_variant_names_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let variants = if let Data::Enum(ref data_enum) = input.data {
data_enum
.variants
.iter()
.map(|v| &v.ident)
.collect::<Vec<_>>()
} else {
panic!("VariantNames can only be derived for enums");
};
let generated = quote! {
impl #name {
pub fn all_variants() -> &'static [&'static str] {
&[
#(stringify!(#variants)),*
]
}
}
};
TokenStream::from(generated)
}
Here’s how it’s used:
#[derive(VariantNames)]
enum Foo {
Bar,
Baz,
}
fn main() {
let variants = Foo::all_variants();
assert_eq!(
Foo::all_variants(),
["Bar", "Baz"]
);
}
Using cargo-expand we can see the code generated by the macro, which basically contains a function returning the same list we would’ve written by hand:
impl Foo {
pub fn all_variants() -> &'static [&'static str] {
&[
"Bar",
"Baz",
]
}
}