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}