]>
Commit | Line | Data |
---|---|---|
1819b194 DM |
1 | //! Tools to create command line parsers |
2 | //! | |
3 | //! This crate provides convenient helpers to create command line | |
4 | //! parsers using Schema definitions. | |
5 | //! | |
6 | //! ## Features | |
7 | //! | |
8 | //! - Use declarative API schema to define the CLI | |
9 | //! - Automatic parameter verification | |
10 | //! - Automatically generate documentation and manual pages | |
11 | //! - Automatically generate bash completion helpers | |
12 | //! - Ability to create interactive commands (using ``rustyline``) | |
13 | //! - Supports complex/nested commands | |
14 | ||
41f3fdfe WB |
15 | use std::collections::HashMap; |
16 | ||
17 | use crate::ApiMethod; | |
18 | ||
1819b194 DM |
19 | mod environment; |
20 | pub use environment::*; | |
21 | ||
22 | mod shellword; | |
23 | pub use shellword::*; | |
24 | ||
25 | mod format; | |
26 | pub use format::*; | |
27 | ||
ce53818e DM |
28 | mod text_table; |
29 | pub use text_table::*; | |
30 | ||
1819b194 | 31 | mod completion; |
1819b194 | 32 | |
417b7159 DM |
33 | mod completion_helpers; |
34 | pub use completion_helpers::*; | |
35 | ||
1819b194 DM |
36 | mod getopts; |
37 | pub use getopts::*; | |
38 | ||
39 | mod command; | |
40 | pub use command::*; | |
41 | ||
42 | mod readline; | |
43 | pub use readline::*; | |
44 | ||
1819b194 DM |
45 | /// Completion function for single parameters. |
46 | /// | |
47 | /// Completion functions gets the current parameter value, and should | |
48 | /// return a list of all possible values. | |
49 | pub type CompletionFunction = fn(&str, &HashMap<String, String>) -> Vec<String>; | |
50 | ||
32b8ae98 HL |
51 | /// Initialize default logger for CLI binaries |
52 | pub fn init_cli_logger(env_var_name: &str, default_log_level: &str) { | |
f5052400 FG |
53 | env_logger::Builder::from_env( |
54 | env_logger::Env::new().filter_or(env_var_name, default_log_level), | |
55 | ) | |
56 | .write_style(env_logger::WriteStyle::Never) | |
57 | .format_level(false) | |
58 | .format_module_path(false) | |
59 | .format_target(false) | |
60 | .format_timestamp(None) | |
61 | .init(); | |
32b8ae98 HL |
62 | } |
63 | ||
1819b194 DM |
64 | /// Define a simple CLI command. |
65 | pub struct CliCommand { | |
66 | /// The Schema definition. | |
67 | pub info: &'static ApiMethod, | |
68 | /// Argument parameter list. | |
69 | /// | |
70 | /// Those parameters are expected to be passed as command line | |
71 | /// arguments in the specified order. All other parameters needs | |
72 | /// to be specified as ``--option <value>`` pairs. | |
73 | pub arg_param: &'static [&'static str], | |
74 | /// Predefined parameters. | |
75 | pub fixed_param: HashMap<&'static str, String>, | |
76 | /// Completion functions. | |
77 | /// | |
78 | /// Each parameter may have an associated completion function, | |
79 | /// which is called by the shell completion handler. | |
80 | pub completion_functions: HashMap<String, CompletionFunction>, | |
81 | } | |
82 | ||
83 | impl CliCommand { | |
1819b194 DM |
84 | /// Create a new instance. |
85 | pub fn new(info: &'static ApiMethod) -> Self { | |
86 | Self { | |
92ffe4c2 WB |
87 | info, |
88 | arg_param: &[], | |
1819b194 DM |
89 | fixed_param: HashMap::new(), |
90 | completion_functions: HashMap::new(), | |
91 | } | |
92 | } | |
93 | ||
94 | /// Set argument parameter list. | |
95 | pub fn arg_param(mut self, names: &'static [&'static str]) -> Self { | |
96 | self.arg_param = names; | |
97 | self | |
98 | } | |
99 | ||
100 | /// Set fixed parameters. | |
101 | pub fn fixed_param(mut self, key: &'static str, value: String) -> Self { | |
102 | self.fixed_param.insert(key, value); | |
103 | self | |
104 | } | |
105 | ||
106 | /// Set completion functions. | |
92ffe4c2 | 107 | pub fn completion_cb(mut self, param_name: &str, cb: CompletionFunction) -> Self { |
1819b194 DM |
108 | self.completion_functions.insert(param_name.into(), cb); |
109 | self | |
110 | } | |
111 | } | |
112 | ||
113 | /// Define nested CLI commands. | |
ee1a7cd6 | 114 | #[derive(Default)] |
1819b194 DM |
115 | pub struct CliCommandMap { |
116 | /// Each command has an unique name. The map associates names with | |
117 | /// command definitions. | |
118 | pub commands: HashMap<String, CommandLineInterface>, | |
e7cb1f20 | 119 | pub aliases: Vec<(Vec<&'static str>, Vec<&'static str>)>, |
d014c6f2 DM |
120 | /// List of options to suppress in generate_usage |
121 | pub usage_skip_options: &'static [&'static str], | |
1819b194 DM |
122 | } |
123 | ||
124 | impl CliCommandMap { | |
1819b194 DM |
125 | /// Create a new instance. |
126 | pub fn new() -> Self { | |
ee1a7cd6 | 127 | Default::default() |
1819b194 DM |
128 | } |
129 | ||
130 | /// Insert another command. | |
f1fcdff9 | 131 | pub fn insert<C: Into<CommandLineInterface>>(mut self, name: &'static str, cli: C) -> Self { |
bf7b939b | 132 | self.commands.insert(name.into(), cli.into()); |
1819b194 DM |
133 | self |
134 | } | |
135 | ||
b210ad69 | 136 | pub fn alias(mut self, old: &'static [&'static str], new: &'static [&'static str]) -> Self { |
e7cb1f20 DM |
137 | self.aliases.push((Vec::from(old), Vec::from(new))); |
138 | self | |
139 | } | |
140 | ||
d014c6f2 DM |
141 | pub fn usage_skip_options(mut self, list: &'static [&'static str]) -> Self { |
142 | self.usage_skip_options = list; | |
143 | self | |
144 | } | |
145 | ||
1819b194 DM |
146 | /// Insert the help command. |
147 | pub fn insert_help(mut self) -> Self { | |
92ffe4c2 WB |
148 | self.commands |
149 | .insert(String::from("help"), help_command_def().into()); | |
1819b194 DM |
150 | self |
151 | } | |
152 | ||
153 | fn find_command(&self, name: &str) -> Option<(String, &CommandLineInterface)> { | |
1819b194 DM |
154 | if let Some(sub_cmd) = self.commands.get(name) { |
155 | return Some((name.to_string(), sub_cmd)); | |
156 | }; | |
157 | ||
158 | let mut matches: Vec<&str> = vec![]; | |
159 | ||
160 | for cmd in self.commands.keys() { | |
161 | if cmd.starts_with(name) { | |
92ffe4c2 WB |
162 | matches.push(cmd); |
163 | } | |
1819b194 DM |
164 | } |
165 | ||
92ffe4c2 WB |
166 | if matches.len() != 1 { |
167 | return None; | |
168 | } | |
1819b194 DM |
169 | |
170 | if let Some(sub_cmd) = self.commands.get(matches[0]) { | |
171 | return Some((matches[0].to_string(), sub_cmd)); | |
172 | }; | |
173 | ||
174 | None | |
175 | } | |
176 | } | |
177 | ||
178 | /// Define Complex command line interfaces. | |
179 | pub enum CommandLineInterface { | |
180 | Simple(CliCommand), | |
181 | Nested(CliCommandMap), | |
182 | } | |
183 | ||
184 | impl From<CliCommand> for CommandLineInterface { | |
185 | fn from(cli_cmd: CliCommand) -> Self { | |
92ffe4c2 | 186 | CommandLineInterface::Simple(cli_cmd) |
1819b194 DM |
187 | } |
188 | } | |
189 | ||
190 | impl From<CliCommandMap> for CommandLineInterface { | |
191 | fn from(list: CliCommandMap) -> Self { | |
192 | CommandLineInterface::Nested(list) | |
193 | } | |
194 | } |