]> git.proxmox.com Git - rustc.git/blob - vendor/clap/src/output/usage.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / vendor / clap / src / output / usage.rs
1 use indexmap::IndexSet;
2
3 // Internal
4 use crate::builder::AppSettings as AS;
5 use crate::builder::{Arg, ArgPredicate, Command};
6 use crate::parser::ArgMatcher;
7 use crate::util::ChildGraph;
8 use crate::util::Id;
9 use crate::INTERNAL_ERROR_MSG;
10
11 pub(crate) struct Usage<'help, 'cmd> {
12 cmd: &'cmd Command<'help>,
13 required: Option<&'cmd ChildGraph<Id>>,
14 }
15
16 impl<'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 }
157 let usage = usage.trim().to_owned();
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,
336 ) -> IndexSet<String> {
337 debug!(
338 "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
339 incls,
340 matcher.is_some(),
341 incl_last
342 );
343 let mut ret_val = IndexSet::new();
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 mut required_groups_members = IndexSet::new();
386 let mut required_opts = IndexSet::new();
387 let mut required_groups = IndexSet::new();
388 let mut required_positionals = Vec::new();
389 for req in unrolled_reqs.iter().chain(incls.iter()) {
390 if let Some(arg) = self.cmd.find(req) {
391 let is_present = matcher
392 .map(|m| m.check_explicit(req, ArgPredicate::IsPresent))
393 .unwrap_or(false);
394 debug!(
395 "Usage::get_required_usage_from:iter:{:?} arg is_present={}",
396 req, is_present
397 );
398 if !is_present {
399 if arg.is_positional() {
400 if incl_last || !arg.is_last_set() {
401 required_positionals.push((arg.index.unwrap(), arg.to_string()));
402 }
403 } else {
404 required_opts.insert(arg.to_string());
405 }
406 }
407 } else {
408 debug_assert!(self.cmd.find_group(req).is_some());
409 let group_members = self.cmd.unroll_args_in_group(req);
410 let is_present = matcher
411 .map(|m| {
412 group_members
413 .iter()
414 .any(|arg| m.check_explicit(arg, ArgPredicate::IsPresent))
415 })
416 .unwrap_or(false);
417 debug!(
418 "Usage::get_required_usage_from:iter:{:?} group is_present={}",
419 req, is_present
420 );
421 if !is_present {
422 let elem = self.cmd.format_group(req);
423 required_groups.insert(elem);
424 required_groups_members.extend(
425 group_members
426 .iter()
427 .flat_map(|id| self.cmd.find(id))
428 .map(|arg| arg.to_string()),
429 );
430 }
431 }
432 }
433
434 required_opts.retain(|arg| !required_groups_members.contains(arg));
435 ret_val.extend(required_opts);
436
437 ret_val.extend(required_groups);
438
439 required_positionals.sort_by_key(|(ind, _)| *ind); // sort by index
440 for (_, p) in required_positionals {
441 if !required_groups_members.contains(&p) {
442 ret_val.insert(p);
443 }
444 }
445
446 debug!("Usage::get_required_usage_from: ret_val={:?}", ret_val);
447 ret_val
448 }
449 }