cmd_impl/
lib.rs

1//! Quickly define and implement a command containing serval subcommands.
2
3#![deny(missing_docs, unused_crate_dependencies)]
4
5// proc-macro
6use proc_macro::TokenStream;
7// crates.io
8use syn::*;
9
10/// Quickly define and implement a command containing serval subcommands.
11#[proc_macro_attribute]
12pub fn cmd(_: TokenStream, input: TokenStream) -> TokenStream {
13	let cmd_enum = syn::parse_macro_input!(input as ItemEnum);
14
15	// #[cfg(feature = "debug")]
16	// dbg!(&cmd_enum);
17
18	let ItemEnum {
19		attrs: cmd_attrs, vis: cmd_vis, ident: cmd_name, variants: cmd_variants, ..
20	} = cmd_enum;
21	let cmd_variant_names =
22		cmd_variants.iter().map(|variant| variant.ident.clone()).collect::<Vec<_>>();
23	let cmd_variants = cmd_variants
24		.into_iter()
25		.map(|Variant { attrs, ident, .. }| {
26			let cmd = quote::format_ident!("{ident}Cmd");
27
28			quote::quote! {
29				#(#attrs)*
30				#ident(#cmd)
31			}
32		})
33		.collect::<Vec<_>>();
34
35	quote::quote! {
36		#[derive(Debug, clap::Subcommand)]
37		#(#cmd_attrs)*
38		#cmd_vis enum #cmd_name {
39			#(#cmd_variants,)*
40		}
41		impl #cmd_name {
42			#cmd_vis fn run(&self) -> crate::prelude::Result<()> {
43				match self {
44					#(
45						Self::#cmd_variant_names(cmd) => cmd.run(),
46					)*
47				}
48			}
49		}
50	}
51	.into()
52}