]> git.proxmox.com Git - rustc.git/blame - vendor/clap/src/output/usage.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / vendor / clap / src / output / usage.rs
CommitLineData
04454e1e
FG
1use indexmap::IndexSet;
2
3// Internal
923072b8
FG
4use crate::builder::AppSettings as AS;
5use crate::builder::{Arg, ArgPredicate, Command};
6use crate::parser::ArgMatcher;
04454e1e
FG
7use crate::util::ChildGraph;
8use crate::util::Id;
9use crate::INTERNAL_ERROR_MSG;
10
11pub(crate) struct Usage<'help, 'cmd> {
12 cmd: &'cmd Command<'help>,
13 required: Option<&'cmd ChildGraph<Id>>,
14}
15
16impl<'help, 'cmd> Usage<'help, 'cmd> {
17 pub(crate) fn new(cmd: &'cmd Command<'help>) -> Self {
18 Usage {
19 cmd,
20 required: None,
21 }
22 }
23
24 pub(crate) fn required(mut self, required: &'cmd ChildGraph<Id>) -> Self {
25 self.required = Some(required);
26 self
27 }
28
29 // Creates a usage string for display. This happens just after all arguments were parsed, but before
30 // any subcommands have been parsed (so as to give subcommands their own usage recursively)
31 pub(crate) fn create_usage_with_title(&self, used: &[Id]) -> String {
32 debug!("Usage::create_usage_with_title");
33 let mut usage = String::with_capacity(75);
34 usage.push_str("USAGE:\n ");
35 usage.push_str(&*self.create_usage_no_title(used));
36 usage
37 }
38
39 // Creates a usage string (*without title*) if one was not provided by the user manually.
40 pub(crate) fn create_usage_no_title(&self, used: &[Id]) -> String {
41 debug!("Usage::create_usage_no_title");
42 if let Some(u) = self.cmd.get_override_usage() {
43 String::from(&*u)
44 } else if used.is_empty() {
45 self.create_help_usage(true)
46 } else {
47 self.create_smart_usage(used)
48 }
49 }
50
51 // Creates a usage string for display in help messages (i.e. not for errors)
52 fn create_help_usage(&self, incl_reqs: bool) -> String {
53 debug!("Usage::create_help_usage; incl_reqs={:?}", incl_reqs);
54 let mut usage = String::with_capacity(75);
55 let name = self
56 .cmd
57 .get_usage_name()
58 .or_else(|| self.cmd.get_bin_name())
59 .unwrap_or_else(|| self.cmd.get_name());
60 usage.push_str(name);
61 let req_string = if incl_reqs {
62 self.get_required_usage_from(&[], None, false)
63 .iter()
64 .fold(String::new(), |a, s| a + " " + s)
65 } else {
66 String::new()
67 };
68
69 if self.needs_options_tag() {
70 usage.push_str(" [OPTIONS]");
71 }
72
73 let allow_missing_positional = self.cmd.is_allow_missing_positional_set();
74 if !allow_missing_positional {
75 usage.push_str(&req_string);
76 }
77
78 let has_last = self.cmd.get_positionals().any(|p| p.is_last_set());
79 // places a '--' in the usage string if there are args and options
80 // supporting multiple values
81 if self
82 .cmd
83 .get_non_positionals()
84 .any(|o| o.is_multiple_values_set())
85 && self.cmd.get_positionals().any(|p| !p.is_required_set())
86 && !(self.cmd.has_visible_subcommands() || self.cmd.is_allow_external_subcommands_set())
87 && !has_last
88 {
89 usage.push_str(" [--]");
90 }
91 let not_req_or_hidden =
92 |p: &Arg| (!p.is_required_set() || p.is_last_set()) && !p.is_hide_set();
93 if self.cmd.get_positionals().any(not_req_or_hidden) {
94 if let Some(args_tag) = self.get_args_tag(incl_reqs) {
95 usage.push_str(&*args_tag);
96 } else {
97 usage.push_str(" [ARGS]");
98 }
99 if has_last && incl_reqs {
100 let pos = self
101 .cmd
102 .get_positionals()
103 .find(|p| p.is_last_set())
104 .expect(INTERNAL_ERROR_MSG);
105 debug!("Usage::create_help_usage: '{}' has .last(true)", pos.name);
106 let req = pos.is_required_set();
107 if req && self.cmd.get_positionals().any(|p| !p.is_required_set()) {
108 usage.push_str(" -- <");
109 } else if req {
110 usage.push_str(" [--] <");
111 } else {
112 usage.push_str(" [-- <");
113 }
114 usage.push_str(&*pos.name_no_brackets());
115 usage.push('>');
116 usage.push_str(pos.multiple_str());
117 if !req {
118 usage.push(']');
119 }
120 }
121 }
122
123 if allow_missing_positional {
124 usage.push_str(&req_string);
125 }
126
127 // incl_reqs is only false when this function is called recursively
128 if self.cmd.has_visible_subcommands() && incl_reqs
129 || self.cmd.is_allow_external_subcommands_set()
130 {
131 let placeholder = self.cmd.get_subcommand_value_name().unwrap_or("SUBCOMMAND");
132 #[allow(deprecated)]
133 if self.cmd.is_subcommand_negates_reqs_set()
134 || self.cmd.is_args_conflicts_with_subcommands_set()
135 {
136 usage.push_str("\n ");
137 if !self.cmd.is_args_conflicts_with_subcommands_set() {
138 usage.push_str(&*self.create_help_usage(false));
139 } else {
140 usage.push_str(&*name);
141 }
142 usage.push_str(" <");
143 usage.push_str(placeholder);
144 usage.push('>');
145 } else if self.cmd.is_subcommand_required_set()
146 || self.cmd.is_set(AS::SubcommandRequiredElseHelp)
147 {
148 usage.push_str(" <");
149 usage.push_str(placeholder);
150 usage.push('>');
151 } else {
152 usage.push_str(" [");
153 usage.push_str(placeholder);
154 usage.push(']');
155 }
156 }
923072b8 157 let usage = usage.trim().to_owned();
04454e1e
FG
158 debug!("Usage::create_help_usage: usage={}", usage);
159 usage
160 }
161
162 // Creates a context aware usage string, or "smart usage" from currently used
163 // args, and requirements
164 fn create_smart_usage(&self, used: &[Id]) -> String {
165 debug!("Usage::create_smart_usage");
166 let mut usage = String::with_capacity(75);
167
168 let r_string = self
169 .get_required_usage_from(used, None, true)
170 .iter()
171 .fold(String::new(), |acc, s| acc + " " + s);
172
173 usage.push_str(
174 self.cmd
175 .get_usage_name()
176 .or_else(|| self.cmd.get_bin_name())
177 .unwrap_or_else(|| self.cmd.get_name()),
178 );
179 usage.push_str(&*r_string);
180 if self.cmd.is_subcommand_required_set() {
181 usage.push_str(" <");
182 usage.push_str(self.cmd.get_subcommand_value_name().unwrap_or("SUBCOMMAND"));
183 usage.push('>');
184 }
185 usage.shrink_to_fit();
186 usage
187 }
188
189 // Gets the `[ARGS]` tag for the usage string
190 fn get_args_tag(&self, incl_reqs: bool) -> Option<String> {
191 debug!("Usage::get_args_tag; incl_reqs = {:?}", incl_reqs);
192 let mut count = 0;
193 for pos in self
194 .cmd
195 .get_positionals()
196 .filter(|pos| !pos.is_required_set())
197 .filter(|pos| !pos.is_hide_set())
198 .filter(|pos| !pos.is_last_set())
199 {
200 debug!("Usage::get_args_tag:iter:{}", pos.name);
201 let required = self.cmd.groups_for_arg(&pos.id).any(|grp_s| {
202 debug!("Usage::get_args_tag:iter:{:?}:iter:{:?}", pos.name, grp_s);
203 // if it's part of a required group we don't want to count it
204 self.cmd.get_groups().any(|g| g.required && (g.id == grp_s))
205 });
206 if !required {
207 count += 1;
208 debug!(
209 "Usage::get_args_tag:iter: {} Args not required or hidden",
210 count
211 );
212 }
213 }
214
215 if !self.cmd.is_dont_collapse_args_in_usage_set() && count > 1 {
216 debug!("Usage::get_args_tag:iter: More than one, returning [ARGS]");
217
218 // [ARGS]
219 None
220 } else if count == 1 && incl_reqs {
221 let pos = self
222 .cmd
223 .get_positionals()
224 .find(|pos| {
225 !pos.is_required_set()
226 && !pos.is_hide_set()
227 && !pos.is_last_set()
228 && !self.cmd.groups_for_arg(&pos.id).any(|grp_s| {
229 debug!("Usage::get_args_tag:iter:{:?}:iter:{:?}", pos.name, grp_s);
230 // if it's part of a required group we don't want to count it
231 self.cmd.get_groups().any(|g| g.required && (g.id == grp_s))
232 })
233 })
234 .expect(INTERNAL_ERROR_MSG);
235
236 debug!(
237 "Usage::get_args_tag:iter: Exactly one, returning '{}'",
238 pos.name
239 );
240
241 Some(format!(
242 " [{}]{}",
243 pos.name_no_brackets(),
244 pos.multiple_str()
245 ))
246 } else if self.cmd.is_dont_collapse_args_in_usage_set()
247 && self.cmd.has_positionals()
248 && incl_reqs
249 {
250 debug!("Usage::get_args_tag:iter: Don't collapse returning all");
251 Some(
252 self.cmd
253 .get_positionals()
254 .filter(|pos| !pos.is_required_set())
255 .filter(|pos| !pos.is_hide_set())
256 .filter(|pos| !pos.is_last_set())
257 .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
258 .collect::<Vec<_>>()
259 .join(""),
260 )
261 } else if !incl_reqs {
262 debug!("Usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
263 let highest_req_pos = self
264 .cmd
265 .get_positionals()
266 .filter_map(|pos| {
267 if pos.is_required_set() && !pos.is_last_set() {
268 Some(pos.index)
269 } else {
270 None
271 }
272 })
273 .max()
274 .unwrap_or_else(|| Some(self.cmd.get_positionals().count()));
275 Some(
276 self.cmd
277 .get_positionals()
278 .filter(|pos| pos.index <= highest_req_pos)
279 .filter(|pos| !pos.is_required_set())
280 .filter(|pos| !pos.is_hide_set())
281 .filter(|pos| !pos.is_last_set())
282 .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
283 .collect::<Vec<_>>()
284 .join(""),
285 )
286 } else {
287 Some("".into())
288 }
289 }
290
291 // Determines if we need the `[OPTIONS]` tag in the usage string
292 fn needs_options_tag(&self) -> bool {
293 debug!("Usage::needs_options_tag");
294 'outer: for f in self.cmd.get_non_positionals() {
295 debug!("Usage::needs_options_tag:iter: f={}", f.name);
296
297 // Don't print `[OPTIONS]` just for help or version
298 if f.long == Some("help") || f.long == Some("version") {
299 debug!("Usage::needs_options_tag:iter Option is built-in");
300 continue;
301 }
302
303 if f.is_hide_set() {
304 debug!("Usage::needs_options_tag:iter Option is hidden");
305 continue;
306 }
307 if f.is_required_set() {
308 debug!("Usage::needs_options_tag:iter Option is required");
309 continue;
310 }
311 for grp_s in self.cmd.groups_for_arg(&f.id) {
312 debug!("Usage::needs_options_tag:iter:iter: grp_s={:?}", grp_s);
313 if self.cmd.get_groups().any(|g| g.id == grp_s && g.required) {
314 debug!("Usage::needs_options_tag:iter:iter: Group is required");
315 continue 'outer;
316 }
317 }
318
319 debug!("Usage::needs_options_tag:iter: [OPTIONS] required");
320 return true;
321 }
322
323 debug!("Usage::needs_options_tag: [OPTIONS] not required");
324 false
325 }
326
327 // Returns the required args in usage string form by fully unrolling all groups
328 // `incl_last`: should we include args that are Arg::Last? (i.e. `prog [foo] -- [last]). We
329 // can't do that for required usages being built for subcommands because it would look like:
330 // `prog [foo] -- [last] <subcommand>` which is totally wrong.
331 pub(crate) fn get_required_usage_from(
332 &self,
333 incls: &[Id],
334 matcher: Option<&ArgMatcher>,
335 incl_last: bool,
923072b8 336 ) -> IndexSet<String> {
04454e1e
FG
337 debug!(
338 "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
339 incls,
340 matcher.is_some(),
341 incl_last
342 );
923072b8 343 let mut ret_val = IndexSet::new();
04454e1e
FG
344
345 let mut unrolled_reqs = IndexSet::new();
346
347 let required_owned;
348 let required = if let Some(required) = self.required {
349 required
350 } else {
351 required_owned = self.cmd.required_graph();
352 &required_owned
353 };
354
355 for a in required.iter() {
356 let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option<Id> {
357 let required = match val {
358 ArgPredicate::Equals(_) => {
359 if let Some(matcher) = matcher {
360 matcher.check_explicit(a, *val)
361 } else {
362 false
363 }
364 }
365 ArgPredicate::IsPresent => true,
366 };
367 required.then(|| req_arg.clone())
368 };
369
370 for aa in self.cmd.unroll_arg_requires(is_relevant, a) {
371 // if we don't check for duplicates here this causes duplicate error messages
372 // see https://github.com/clap-rs/clap/issues/2770
373 unrolled_reqs.insert(aa);
374 }
375 // always include the required arg itself. it will not be enumerated
376 // by unroll_requirements_for_arg.
377 unrolled_reqs.insert(a.clone());
378 }
379
380 debug!(
381 "Usage::get_required_usage_from: unrolled_reqs={:?}",
382 unrolled_reqs
383 );
384
385 let args_in_groups = self
386 .cmd
387 .get_groups()
388 .filter(|gn| required.contains(&gn.id))
389 .flat_map(|g| self.cmd.unroll_args_in_group(&g.id))
390 .collect::<Vec<_>>();
391
392 for a in unrolled_reqs
393 .iter()
394 .chain(incls.iter())
395 .filter(|name| !self.cmd.get_positionals().any(|p| &&p.id == name))
396 .filter(|name| !self.cmd.get_groups().any(|g| &&g.id == name))
397 .filter(|name| !args_in_groups.contains(name))
923072b8
FG
398 .filter(|name| {
399 !(matcher.is_some()
400 && matcher
401 .as_ref()
402 .unwrap()
403 .check_explicit(name, ArgPredicate::IsPresent))
404 })
04454e1e
FG
405 {
406 debug!("Usage::get_required_usage_from:iter:{:?}", a);
407 let arg = self.cmd.find(a).expect(INTERNAL_ERROR_MSG).to_string();
923072b8 408 ret_val.insert(arg);
04454e1e
FG
409 }
410 let mut g_vec: Vec<String> = vec![];
411 for g in unrolled_reqs
412 .iter()
413 .filter(|n| self.cmd.get_groups().any(|g| g.id == **n))
414 {
415 // don't print requirement for required groups that have an arg.
416 if let Some(m) = matcher {
417 let have_group_entry = self
418 .cmd
419 .unroll_args_in_group(g)
420 .iter()
923072b8 421 .any(|arg| m.check_explicit(arg, ArgPredicate::IsPresent));
04454e1e
FG
422 if have_group_entry {
423 continue;
424 }
425 }
426
427 let elem = self.cmd.format_group(g);
428 if !g_vec.contains(&elem) {
429 g_vec.push(elem);
430 }
431 }
923072b8 432 ret_val.extend(g_vec);
04454e1e
FG
433
434 let mut pvec = unrolled_reqs
435 .iter()
436 .chain(incls.iter())
437 .filter(|a| self.cmd.get_positionals().any(|p| &&p.id == a))
923072b8
FG
438 .filter(|&pos| {
439 matcher.map_or(true, |m| !m.check_explicit(pos, ArgPredicate::IsPresent))
440 })
04454e1e
FG
441 .filter_map(|pos| self.cmd.find(pos))
442 .filter(|&pos| incl_last || !pos.is_last_set())
443 .filter(|pos| !args_in_groups.contains(&pos.id))
444 .map(|pos| (pos.index.unwrap(), pos))
445 .collect::<Vec<(usize, &Arg)>>();
446 pvec.sort_by_key(|(ind, _)| *ind); // sort by index
447
448 for (_, p) in pvec {
923072b8 449 debug!("Usage::get_required_usage_from:push:{:?}", p.id);
04454e1e 450 if !args_in_groups.contains(&p.id) {
923072b8 451 ret_val.insert(p.to_string());
04454e1e
FG
452 }
453 }
454
455 debug!("Usage::get_required_usage_from: ret_val={:?}", ret_val);
456 ret_val
457 }
458}