shellfish/clap_command.rs
1/// Creates a [`Command`](crate::Command) which takes a [`clap::Parser`] as
2/// input.
3///
4/// This macro creates a wrapper around synchronous or asynchronous functions to
5/// automatically parse and pass the clap argument, and will
6/// return an error to the shell if it cannot be suitably parsed.
7///
8/// ```
9/// #[derive(Parser, Debug)]
10/// #[clap(author, version, about)]
11/// struct Args {
12/// // - snip
13/// }
14///
15/// shell.commands.insert("greet", clap_command!((), Args, greet));
16/// shell.commands.insert("greet-async", clap_command!((), Args, async greet_async));
17///
18/// fn greet(_state: &mut (), args: Args) -> Result<(), Box<dyn std::error::Error>> {
19/// // - snip
20/// }
21///
22/// async fn greet_async(_state: &mut (), args: Args) -> Result<(), Box<std::io::Error>> {
23/// // - snip
24/// }
25/// ```
26///
27/// NOTE: You need the `async` crate feature enabled for async
28/// clap commands.
29#[macro_export]
30macro_rules! clap_command {
31 ($state: ty, $clap: ty, async $inc: expr) => {{
32 async fn func(
33 state: &mut $state,
34 args: Vec<String>,
35 ) -> Result<(), Box<dyn ::std::error::Error>> {
36 let parsed: $clap = match ::clap::Parser::try_parse_from(&args[..])
37 {
38 ::std::result::Result::Ok(o) => o,
39 ::std::result::Result::Err(e)
40 if e.kind == ::clap::error::ErrorKind::DisplayHelp =>
41 {
42 e.print()?;
43 return Ok(());
44 }
45 ::std::result::Result::Err(e) => {
46 return std::result::Result::Err(e.into())
47 }
48 };
49 $inc(state, parsed).await?;
50 Ok(())
51 }
52 let command = $crate::Command::new_async(
53 <$clap as ::clap::CommandFactory>::into_app()
54 .get_about()
55 .unwrap_or_default()
56 .to_string(),
57 $crate::async_fn!($state, func),
58 );
59 command
60 }};
61 ($state: ty, $clap: ty, $inc: expr) => {{
62 fn func(
63 state: &mut $state,
64 args: Vec<String>,
65 ) -> Result<(), Box<dyn ::std::error::Error>> {
66 let parsed: $clap = match ::clap::Parser::try_parse_from(&args[..])
67 {
68 ::std::result::Result::Ok(o) => o,
69 ::std::result::Result::Err(e)
70 if e.kind == ::clap::error::ErrorKind::DisplayHelp =>
71 {
72 e.print()?;
73 return Ok(());
74 }
75 ::std::result::Result::Err(e) => {
76 return std::result::Result::Err(e.into())
77 }
78 };
79 $inc(state, parsed)?;
80 Ok(())
81 }
82 let command = $crate::Command::new(
83 <$clap as ::clap::CommandFactory>::into_app()
84 .get_about()
85 .unwrap_or_default()
86 .to_string(),
87 func,
88 );
89 command
90 }};
91}