]>
Commit | Line | Data |
---|---|---|
04454e1e FG |
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 in cmd.get_subcommands().map(all_subcommands) { | |
13 | subcmds.extend(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<'help, 'cmd>( | |
23 | p: &'cmd Command<'help>, | |
24 | path: Vec<&str>, | |
25 | ) -> &'cmd Command<'help> { | |
26 | let mut cmd = p; | |
27 | ||
28 | for sc in path { | |
29 | cmd = cmd.find_subcommand(sc).unwrap(); | |
30 | } | |
31 | ||
32 | cmd | |
33 | } | |
34 | ||
35 | /// Gets subcommands of [`clap::Command`] in the form of `("name", "bin_name")`. | |
36 | /// | |
37 | /// Subcommand `rustup toolchain install` would be converted to | |
38 | /// `("install", "rustup toolchain install")`. | |
39 | pub fn subcommands(p: &Command) -> Vec<(String, String)> { | |
40 | debug!("subcommands: name={}", p.get_name()); | |
41 | debug!("subcommands: Has subcommands...{:?}", p.has_subcommands()); | |
42 | ||
43 | let mut subcmds = vec![]; | |
44 | ||
45 | if !p.has_subcommands() { | |
46 | return subcmds; | |
47 | } | |
48 | ||
49 | for sc in p.get_subcommands() { | |
50 | let sc_bin_name = sc.get_bin_name().unwrap(); | |
51 | ||
52 | debug!( | |
53 | "subcommands:iter: name={}, bin_name={}", | |
54 | sc.get_name(), | |
55 | sc_bin_name | |
56 | ); | |
57 | ||
58 | subcmds.push((sc.get_name().to_string(), sc_bin_name.to_string())); | |
59 | } | |
60 | ||
61 | subcmds | |
62 | } | |
63 | ||
64 | /// Gets all the short options, their visible aliases and flags of a [`clap::Command`]. | |
65 | /// Includes `h` and `V` depending on the [`clap::AppSettings`]. | |
66 | pub 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 | ||
89 | /// Gets all the long options, their visible aliases and flags of a [`clap::Command`]. | |
90 | /// Includes `help` and `version` depending on the [`clap::AppSettings`]. | |
91 | pub 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 | ||
119 | /// Gets all the flags of a [`clap::Command`](Command). | |
120 | /// Includes `help` and `version` depending on the [`clap::AppSettings`]. | |
121 | pub fn flags<'help>(p: &Command<'help>) -> Vec<Arg<'help>> { | |
122 | debug!("flags: name={}", p.get_name()); | |
123 | p.get_arguments() | |
124 | .filter(|a| !a.is_takes_value_set() && !a.is_positional()) | |
125 | .cloned() | |
126 | .collect() | |
127 | } | |
128 | ||
129 | #[cfg(test)] | |
130 | mod tests { | |
131 | use super::*; | |
132 | use clap::Arg; | |
133 | use pretty_assertions::assert_eq; | |
134 | ||
135 | fn common_app() -> Command<'static> { | |
136 | Command::new("myapp") | |
137 | .subcommand( | |
138 | Command::new("test").subcommand(Command::new("config")).arg( | |
139 | Arg::new("file") | |
140 | .short('f') | |
141 | .short_alias('c') | |
142 | .visible_short_alias('p') | |
143 | .long("file") | |
144 | .visible_alias("path"), | |
145 | ), | |
146 | ) | |
147 | .subcommand(Command::new("hello")) | |
148 | .bin_name("my-cmd") | |
149 | } | |
150 | ||
151 | fn built() -> Command<'static> { | |
152 | let mut cmd = common_app(); | |
153 | ||
154 | cmd._build_all(); | |
155 | cmd | |
156 | } | |
157 | ||
158 | fn built_with_version() -> Command<'static> { | |
159 | let mut cmd = common_app().version("3.0"); | |
160 | ||
161 | cmd._build_all(); | |
162 | cmd | |
163 | } | |
164 | ||
165 | #[test] | |
166 | fn test_subcommands() { | |
167 | let cmd = built_with_version(); | |
168 | ||
169 | assert_eq!( | |
170 | subcommands(&cmd), | |
171 | vec![ | |
172 | ("test".to_string(), "my-cmd test".to_string()), | |
173 | ("hello".to_string(), "my-cmd hello".to_string()), | |
174 | ("help".to_string(), "my-cmd help".to_string()), | |
175 | ] | |
176 | ); | |
177 | } | |
178 | ||
179 | #[test] | |
180 | fn test_all_subcommands() { | |
181 | let cmd = built_with_version(); | |
182 | ||
183 | assert_eq!( | |
184 | all_subcommands(&cmd), | |
185 | vec![ | |
186 | ("test".to_string(), "my-cmd test".to_string()), | |
187 | ("hello".to_string(), "my-cmd hello".to_string()), | |
188 | ("help".to_string(), "my-cmd help".to_string()), | |
189 | ("config".to_string(), "my-cmd test config".to_string()), | |
190 | ("help".to_string(), "my-cmd test help".to_string()), | |
191 | ] | |
192 | ); | |
193 | } | |
194 | ||
195 | #[test] | |
196 | fn test_find_subcommand_with_path() { | |
197 | let cmd = built_with_version(); | |
198 | let sc_app = find_subcommand_with_path(&cmd, "test config".split(' ').collect()); | |
199 | ||
200 | assert_eq!(sc_app.get_name(), "config"); | |
201 | } | |
202 | ||
203 | #[test] | |
204 | fn test_flags() { | |
205 | let cmd = built_with_version(); | |
206 | let actual_flags = flags(&cmd); | |
207 | ||
208 | assert_eq!(actual_flags.len(), 2); | |
209 | assert_eq!(actual_flags[0].get_long(), Some("help")); | |
210 | assert_eq!(actual_flags[1].get_long(), Some("version")); | |
211 | ||
212 | let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"])); | |
213 | ||
214 | assert_eq!(sc_flags.len(), 2); | |
215 | assert_eq!(sc_flags[0].get_long(), Some("file")); | |
216 | assert_eq!(sc_flags[1].get_long(), Some("help")); | |
217 | } | |
218 | ||
219 | #[test] | |
220 | fn test_flag_subcommand() { | |
221 | let cmd = built(); | |
222 | let actual_flags = flags(&cmd); | |
223 | ||
224 | assert_eq!(actual_flags.len(), 1); | |
225 | assert_eq!(actual_flags[0].get_long(), Some("help")); | |
226 | ||
227 | let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"])); | |
228 | ||
229 | assert_eq!(sc_flags.len(), 2); | |
230 | assert_eq!(sc_flags[0].get_long(), Some("file")); | |
231 | assert_eq!(sc_flags[1].get_long(), Some("help")); | |
232 | } | |
233 | ||
234 | #[test] | |
235 | fn test_shorts() { | |
236 | let cmd = built_with_version(); | |
237 | let shorts = shorts_and_visible_aliases(&cmd); | |
238 | ||
239 | assert_eq!(shorts.len(), 2); | |
240 | assert_eq!(shorts[0], 'h'); | |
241 | assert_eq!(shorts[1], 'V'); | |
242 | ||
243 | let sc_shorts = shorts_and_visible_aliases(find_subcommand_with_path(&cmd, vec!["test"])); | |
244 | ||
245 | assert_eq!(sc_shorts.len(), 3); | |
246 | assert_eq!(sc_shorts[0], 'p'); | |
247 | assert_eq!(sc_shorts[1], 'f'); | |
248 | assert_eq!(sc_shorts[2], 'h'); | |
249 | } | |
250 | ||
251 | #[test] | |
252 | fn test_longs() { | |
253 | let cmd = built_with_version(); | |
254 | let longs = longs_and_visible_aliases(&cmd); | |
255 | ||
256 | assert_eq!(longs.len(), 2); | |
257 | assert_eq!(longs[0], "help"); | |
258 | assert_eq!(longs[1], "version"); | |
259 | ||
260 | let sc_longs = longs_and_visible_aliases(find_subcommand_with_path(&cmd, vec!["test"])); | |
261 | ||
262 | assert_eq!(sc_longs.len(), 3); | |
263 | assert_eq!(sc_longs[0], "path"); | |
264 | assert_eq!(sc_longs[1], "file"); | |
265 | assert_eq!(sc_longs[2], "help"); | |
266 | } | |
267 | } |