shared/extensions/
commands.rs1use clap::{Arg, ArgMatches, Args, Command};
2use std::{collections::HashMap, pin::Pin, sync::Arc};
3
4pub type ExecutorFunc = dyn Fn(
5 Option<Arc<crate::env::Env>>,
6 ArgMatches,
7 ) -> Pin<Box<dyn Future<Output = Result<i32, anyhow::Error>>>>
8 + Send;
9
10pub enum CommandMapEntry {
11 Command(Box<ExecutorFunc>),
12 Group(HashMap<&'static str, CommandMapEntry>),
13}
14
15pub struct CliCommandGroupBuilder {
16 command: Command,
17 map: HashMap<&'static str, CommandMapEntry>,
18}
19
20impl CliCommandGroupBuilder {
21 pub fn new(name: &'static str, about: &'static str) -> Self {
22 Self {
23 command: Command::new(name)
24 .version(crate::full_version())
25 .arg(
26 Arg::new("debug")
27 .help("pass in order to run in debug mode")
28 .num_args(0)
29 .short('d')
30 .long("debug")
31 .default_value("false")
32 .value_parser(clap::value_parser!(bool))
33 .global(true)
34 .required(false),
35 )
36 .about(about),
37 map: HashMap::new(),
38 }
39 }
40
41 pub fn get_command(&self) -> Command {
42 self.command.clone()
43 }
44
45 pub fn get_matches(&mut self) -> ArgMatches {
46 self.command.get_matches_mut()
47 }
48
49 pub fn print_help(&mut self) {
50 let _ = self.command.print_long_help();
51 }
52
53 pub fn match_command(
54 &self,
55 command: String,
56 arg_matches: ArgMatches,
57 ) -> Option<(&ExecutorFunc, ArgMatches)> {
58 let mut current_map = &self.map;
59 let mut current_matches = arg_matches;
60 let mut current_command = command;
61
62 loop {
63 let entry = current_map.get(current_command.as_str())?;
64
65 match entry {
66 CommandMapEntry::Command(executor) => {
67 return Some((executor, current_matches));
68 }
69 CommandMapEntry::Group(submap) => {
70 let (subcommand_name, subcommand_matches) =
71 current_matches.remove_subcommand()?;
72
73 current_map = submap;
74 current_matches = subcommand_matches;
75 current_command = subcommand_name;
76 }
77 }
78 }
79 }
80
81 pub fn add_group<F: FnOnce(CliCommandGroupBuilder) -> CliCommandGroupBuilder>(
82 mut self,
83 name: &'static str,
84 about: &'static str,
85 callback: F,
86 ) -> Self {
87 let subgroup = CliCommandGroupBuilder::new(name, about);
88 let subgroup = callback(subgroup);
89
90 self.command = self.command.subcommand(subgroup.command);
91 self.map.insert(name, CommandMapEntry::Group(subgroup.map));
92
93 self
94 }
95
96 pub fn add_command<A: Args>(
97 mut self,
98 name: &'static str,
99 about: &'static str,
100 cli_command: impl CliCommand<A>,
101 ) -> Self {
102 let command = cli_command.get_command(Command::new(name).about(about));
103 let command = A::augment_args(command);
104
105 self.command = self.command.subcommand(command);
106 self.map
107 .insert(name, CommandMapEntry::Command(cli_command.get_executor()));
108
109 self
110 }
111}
112
113pub trait CliCommand<A: Args> {
114 fn get_command(&self, command: Command) -> Command;
115 fn get_executor(self) -> Box<ExecutorFunc>;
116}