clap_complete/generator/
utils.rs1use clap::{Arg, Command};
4
5pub fn all_subcommands(cmd: &Command) -> Vec<(String, String)> {
10 let mut subcmds: Vec<_> = subcommands(cmd);
11
12 for sc_v in cmd.get_subcommands().map(all_subcommands) {
13 subcmds.extend(sc_v);
14 }
15
16 subcmds
17}
18
19pub fn find_subcommand_with_path<'cmd>(p: &'cmd Command, path: Vec<&str>) -> &'cmd Command {
23 let mut cmd = p;
24
25 for sc in path {
26 cmd = cmd.find_subcommand(sc).unwrap();
27 }
28
29 cmd
30}
31
32pub fn subcommands(p: &Command) -> Vec<(String, String)> {
37 debug!("subcommands: name={}", p.get_name());
38 debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
39
40 let mut subcmds = vec![];
41
42 for sc in p.get_subcommands() {
43 let sc_bin_name = sc.get_bin_name().unwrap();
44
45 debug!(
46 "subcommands:iter: name={}, bin_name={}",
47 sc.get_name(),
48 sc_bin_name
49 );
50 subcmds.push((sc.get_name().to_string(), sc_bin_name.to_string()));
51
52 for alias in sc.get_visible_aliases() {
53 debug!(
54 "subcommands:iter: alias={}, bin_name={}",
55 alias, sc_bin_name
56 );
57 subcmds.push((alias.to_string(), sc_bin_name.to_string()));
58 }
59 }
60
61 subcmds
62}
63
64pub fn shorts_and_visible_aliases(p: &Command) -> Vec<char> {
67 debug!("shorts: name={}", p.get_name());
68
69 p.get_arguments()
70 .filter_map(|a| {
71 if !a.is_positional() {
72 if a.get_visible_short_aliases().is_some() && a.get_short().is_some() {
73 let mut shorts_and_visible_aliases = a.get_visible_short_aliases().unwrap();
74 shorts_and_visible_aliases.push(a.get_short().unwrap());
75 Some(shorts_and_visible_aliases)
76 } else if a.get_visible_short_aliases().is_none() && a.get_short().is_some() {
77 Some(vec![a.get_short().unwrap()])
78 } else {
79 None
80 }
81 } else {
82 None
83 }
84 })
85 .flatten()
86 .collect()
87}
88
89pub fn longs_and_visible_aliases(p: &Command) -> Vec<String> {
92 debug!("longs: name={}", p.get_name());
93
94 p.get_arguments()
95 .filter_map(|a| {
96 if !a.is_positional() {
97 if a.get_visible_aliases().is_some() && a.get_long().is_some() {
98 let mut visible_aliases: Vec<_> = a
99 .get_visible_aliases()
100 .unwrap()
101 .into_iter()
102 .map(|s| s.to_string())
103 .collect();
104 visible_aliases.push(a.get_long().unwrap().to_string());
105 Some(visible_aliases)
106 } else if a.get_visible_aliases().is_none() && a.get_long().is_some() {
107 Some(vec![a.get_long().unwrap().to_string()])
108 } else {
109 None
110 }
111 } else {
112 None
113 }
114 })
115 .flatten()
116 .collect()
117}
118
119pub fn flags(p: &Command) -> Vec<Arg> {
122 debug!("flags: name={}", p.get_name());
123 p.get_arguments()
124 .filter(|a| !a.get_num_args().expect("built").takes_values() && !a.is_positional())
125 .cloned()
126 .collect()
127}
128
129pub fn possible_values(a: &Arg) -> Option<Vec<clap::builder::PossibleValue>> {
131 if !a.get_num_args().expect("built").takes_values() {
132 None
133 } else {
134 a.get_value_parser()
135 .possible_values()
136 .map(|pvs| pvs.collect())
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use clap::Arg;
144 use clap::ArgAction;
145
146 fn common_app() -> Command {
147 Command::new("myapp")
148 .subcommand(
149 Command::new("test").subcommand(Command::new("config")).arg(
150 Arg::new("file")
151 .short('f')
152 .short_alias('c')
153 .visible_short_alias('p')
154 .long("file")
155 .action(ArgAction::SetTrue)
156 .visible_alias("path"),
157 ),
158 )
159 .subcommand(Command::new("hello"))
160 .bin_name("my-cmd")
161 }
162
163 fn built() -> Command {
164 let mut cmd = common_app();
165
166 cmd.build();
167 cmd
168 }
169
170 fn built_with_version() -> Command {
171 let mut cmd = common_app().version("3.0");
172
173 cmd.build();
174 cmd
175 }
176
177 #[test]
178 fn test_subcommands() {
179 let cmd = built_with_version();
180
181 assert_eq!(
182 subcommands(&cmd),
183 vec![
184 ("test".to_string(), "my-cmd test".to_string()),
185 ("hello".to_string(), "my-cmd hello".to_string()),
186 ("help".to_string(), "my-cmd help".to_string()),
187 ]
188 );
189 }
190
191 #[test]
192 fn test_all_subcommands() {
193 let cmd = built_with_version();
194
195 assert_eq!(
196 all_subcommands(&cmd),
197 vec![
198 ("test".to_string(), "my-cmd test".to_string()),
199 ("hello".to_string(), "my-cmd hello".to_string()),
200 ("help".to_string(), "my-cmd help".to_string()),
201 ("config".to_string(), "my-cmd test config".to_string()),
202 ("help".to_string(), "my-cmd test help".to_string()),
203 ("config".to_string(), "my-cmd test help config".to_string()),
204 ("help".to_string(), "my-cmd test help help".to_string()),
205 ("test".to_string(), "my-cmd help test".to_string()),
206 ("hello".to_string(), "my-cmd help hello".to_string()),
207 ("help".to_string(), "my-cmd help help".to_string()),
208 ("config".to_string(), "my-cmd help test config".to_string()),
209 ]
210 );
211 }
212
213 #[test]
214 fn test_find_subcommand_with_path() {
215 let cmd = built_with_version();
216 let sc_app = find_subcommand_with_path(&cmd, "test config".split(' ').collect());
217
218 assert_eq!(sc_app.get_name(), "config");
219 }
220
221 #[test]
222 fn test_flags() {
223 let cmd = built_with_version();
224 let actual_flags = flags(&cmd);
225
226 assert_eq!(actual_flags.len(), 2);
227 assert_eq!(actual_flags[0].get_long(), Some("help"));
228 assert_eq!(actual_flags[1].get_long(), Some("version"));
229
230 let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"]));
231
232 assert_eq!(sc_flags.len(), 2);
233 assert_eq!(sc_flags[0].get_long(), Some("file"));
234 assert_eq!(sc_flags[1].get_long(), Some("help"));
235 }
236
237 #[test]
238 fn test_flag_subcommand() {
239 let cmd = built();
240 let actual_flags = flags(&cmd);
241
242 assert_eq!(actual_flags.len(), 1);
243 assert_eq!(actual_flags[0].get_long(), Some("help"));
244
245 let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"]));
246
247 assert_eq!(sc_flags.len(), 2);
248 assert_eq!(sc_flags[0].get_long(), Some("file"));
249 assert_eq!(sc_flags[1].get_long(), Some("help"));
250 }
251
252 #[test]
253 fn test_shorts() {
254 let cmd = built_with_version();
255 let shorts = shorts_and_visible_aliases(&cmd);
256
257 assert_eq!(shorts.len(), 2);
258 assert_eq!(shorts[0], 'h');
259 assert_eq!(shorts[1], 'V');
260
261 let sc_shorts = shorts_and_visible_aliases(find_subcommand_with_path(&cmd, vec!["test"]));
262
263 assert_eq!(sc_shorts.len(), 3);
264 assert_eq!(sc_shorts[0], 'p');
265 assert_eq!(sc_shorts[1], 'f');
266 assert_eq!(sc_shorts[2], 'h');
267 }
268
269 #[test]
270 fn test_longs() {
271 let cmd = built_with_version();
272 let longs = longs_and_visible_aliases(&cmd);
273
274 assert_eq!(longs.len(), 2);
275 assert_eq!(longs[0], "help");
276 assert_eq!(longs[1], "version");
277
278 let sc_longs = longs_and_visible_aliases(find_subcommand_with_path(&cmd, vec!["test"]));
279
280 assert_eq!(sc_longs.len(), 3);
281 assert_eq!(sc_longs[0], "path");
282 assert_eq!(sc_longs[1], "file");
283 assert_eq!(sc_longs[2], "help");
284 }
285}