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