abscissa_core/
command.rs

1//! Application (sub)command(s), i.e. app entry points
2
3#[doc(hidden)]
4pub use abscissa_derive::Command;
5
6use crate::{runnable::Runnable, terminal};
7use clap::{FromArgMatches, Parser};
8use std::{env, ffi::OsString, fmt::Debug};
9use termcolor::ColorChoice;
10
11/// Subcommand of an application: derives or otherwise implements the `Options`
12/// trait, but also has a `run()` method which can be used to invoke the given
13/// (sub)command.
14pub trait Command: Debug + FromArgMatches + Runnable {
15    /// Name of this program as a string
16    fn name() -> &'static str;
17
18    /// Description of this program
19    fn description() -> &'static str;
20
21    /// Authors of this program
22    fn authors() -> &'static str;
23
24    /// Parse command-line arguments from an iterator
25    fn parse_args<T, I>(into_args: I) -> Self
26    where
27        Self: Parser,
28        I: IntoIterator<Item = T>,
29        T: Into<OsString> + Clone,
30    {
31        let args: Vec<OsString> = into_args.into_iter().map(|s| s.into()).collect();
32
33        Self::try_parse_from(args.as_slice()).unwrap_or_else(|err| {
34            terminal::init(ColorChoice::Auto);
35            err.exit()
36        })
37    }
38
39    /// Parse command-line arguments from the environment
40    fn parse_env_args() -> Self
41    where
42        Self: Parser,
43    {
44        Self::parse_args(env::args())
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use crate::{Command, Runnable};
51    use clap::Parser;
52
53    #[derive(Command, Debug, Parser)]
54    pub struct DummyCommand {}
55
56    impl Runnable for DummyCommand {
57        fn run(&self) {
58            panic!("unimplemented");
59        }
60    }
61
62    #[test]
63    fn derived_command_test() {
64        assert_eq!(DummyCommand::name(), "abscissa_core");
65    }
66}