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