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 .arg(
25 Arg::new("debug")
26 .help("pass in order to run in debug mode")
27 .num_args(0)
28 .short('d')
29 .long("debug")
30 .default_value("false")
31 .value_parser(clap::value_parser!(bool))
32 .global(true)
33 .required(false),
34 )
35 .about(about),
36 map: HashMap::new(),
37 }
38 }
39
40 pub fn get_matches(&mut self) -> ArgMatches {
41 self.command.get_matches_mut()
42 }
43
44 pub fn print_help(&mut self) {
45 let _ = self.command.print_long_help();
46 }
47
48 pub fn match_command(
49 &self,
50 command: String,
51 arg_matches: ArgMatches,
52 ) -> Option<(&ExecutorFunc, ArgMatches)> {
53 let mut current_map = &self.map;
54 let mut current_matches = arg_matches;
55 let mut current_command = command;
56
57 loop {
58 let entry = current_map.get(current_command.as_str())?;
59
60 match entry {
61 CommandMapEntry::Command(executor) => {
62 return Some((executor, current_matches));
63 }
64 CommandMapEntry::Group(submap) => {
65 let (subcommand_name, subcommand_matches) =
66 current_matches.remove_subcommand()?;
67
68 current_map = submap;
69 current_matches = subcommand_matches;
70 current_command = subcommand_name;
71 }
72 }
73 }
74 }
75
76 pub fn add_group<F: FnOnce(CliCommandGroupBuilder) -> CliCommandGroupBuilder>(
77 mut self,
78 name: &'static str,
79 about: &'static str,
80 callback: F,
81 ) -> Self {
82 let subgroup = CliCommandGroupBuilder::new(name, about);
83 let subgroup = callback(subgroup);
84
85 self.command = self.command.subcommand(subgroup.command);
86 self.map.insert(name, CommandMapEntry::Group(subgroup.map));
87
88 self
89 }
90
91 pub fn add_command<A: Args>(
92 mut self,
93 name: &'static str,
94 about: &'static str,
95 cli_command: impl CliCommand<A>,
96 ) -> Self {
97 let command = cli_command.get_command(Command::new(name).about(about));
98 let command = A::augment_args(command);
99
100 self.command = self.command.subcommand(command);
101 self.map
102 .insert(name, CommandMapEntry::Command(cli_command.get_executor()));
103
104 self
105 }
106}
107
108pub trait CliCommand<A: Args> {
109 fn get_command(&self, command: Command) -> Command;
110 fn get_executor(self) -> Box<ExecutorFunc>;
111}