1 use indexmap
::IndexSet
;
4 use crate::builder
::AppSettings
as AS
;
5 use crate::builder
::{Arg, ArgPredicate, Command}
;
6 use crate::parser
::ArgMatcher
;
7 use crate::util
::ChildGraph
;
9 use crate::INTERNAL_ERROR_MSG
;
11 pub(crate) struct Usage
<'help
, 'cmd
> {
12 cmd
: &'cmd Command
<'help
>,
13 required
: Option
<&'cmd ChildGraph
<Id
>>,
16 impl<'help
, 'cmd
> Usage
<'help
, 'cmd
> {
17 pub(crate) fn new(cmd
: &'cmd Command
<'help
>) -> Self {
24 pub(crate) fn required(mut self, required
: &'cmd ChildGraph
<Id
>) -> Self {
25 self.required
= Some(required
);
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
));
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() {
44 } else if used
.is_empty() {
45 self.create_help_usage(true)
47 self.create_smart_usage(used
)
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);
58 .or_else(|| self.cmd
.get_bin_name())
59 .unwrap_or_else(|| self.cmd
.get_name());
61 let req_string
= if incl_reqs
{
62 self.get_required_usage_from(&[], None
, false)
64 .fold(String
::new(), |a
, s
| a
+ " " + s
)
69 if self.needs_options_tag() {
70 usage
.push_str(" [OPTIONS]");
73 let allow_missing_positional
= self.cmd
.is_allow_missing_positional_set();
74 if !allow_missing_positional
{
75 usage
.push_str(&req_string
);
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
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())
89 usage
.push_str(" [--]");
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
);
97 usage
.push_str(" [ARGS]");
99 if has_last
&& incl_reqs
{
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(" -- <");
110 usage
.push_str(" [--] <");
112 usage
.push_str(" [-- <");
114 usage
.push_str(&*pos
.name_no_brackets());
116 usage
.push_str(pos
.multiple_str());
123 if allow_missing_positional
{
124 usage
.push_str(&req_string
);
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()
131 let placeholder
= self.cmd
.get_subcommand_value_name().unwrap_or("SUBCOMMAND");
133 if self.cmd
.is_subcommand_negates_reqs_set()
134 || self.cmd
.is_args_conflicts_with_subcommands_set()
136 usage
.push_str("\n ");
137 if !self.cmd
.is_args_conflicts_with_subcommands_set() {
138 usage
.push_str(&*self.create_help_usage(false));
140 usage
.push_str(&*name
);
142 usage
.push_str(" <");
143 usage
.push_str(placeholder
);
145 } else if self.cmd
.is_subcommand_required_set()
146 || self.cmd
.is_set(AS
::SubcommandRequiredElseHelp
)
148 usage
.push_str(" <");
149 usage
.push_str(placeholder
);
152 usage
.push_str(" [");
153 usage
.push_str(placeholder
);
157 let usage
= usage
.trim().to_owned();
158 debug
!("Usage::create_help_usage: usage={}", usage
);
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);
169 .get_required_usage_from(used
, None
, true)
171 .fold(String
::new(), |acc
, s
| acc
+ " " + s
);
176 .or_else(|| self.cmd
.get_bin_name())
177 .unwrap_or_else(|| self.cmd
.get_name()),
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"));
185 usage
.shrink_to_fit();
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
);
196 .filter(|pos
| !pos
.is_required_set())
197 .filter(|pos
| !pos
.is_hide_set())
198 .filter(|pos
| !pos
.is_last_set())
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
))
209 "Usage::get_args_tag:iter: {} Args not required or hidden",
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]");
220 } else if count
== 1 && incl_reqs
{
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
))
234 .expect(INTERNAL_ERROR_MSG
);
237 "Usage::get_args_tag:iter: Exactly one, returning '{}'",
243 pos
.name_no_brackets(),
246 } else if self.cmd
.is_dont_collapse_args_in_usage_set()
247 && self.cmd
.has_positionals()
250 debug
!("Usage::get_args_tag:iter: Don't collapse returning all");
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()))
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
267 if pos
.is_required_set() && !pos
.is_last_set() {
274 .unwrap_or_else(|| Some(self.cmd
.get_positionals().count()));
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()))
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
);
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");
304 debug
!("Usage::needs_options_tag:iter Option is hidden");
307 if f
.is_required_set() {
308 debug
!("Usage::needs_options_tag:iter Option is required");
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");
319 debug
!("Usage::needs_options_tag:iter: [OPTIONS] required");
323 debug
!("Usage::needs_options_tag: [OPTIONS] not required");
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(
334 matcher
: Option
<&ArgMatcher
>,
336 ) -> IndexSet
<String
> {
338 "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
343 let mut ret_val
= IndexSet
::new();
345 let mut unrolled_reqs
= IndexSet
::new();
348 let required
= if let Some(required
) = self.required
{
351 required_owned
= self.cmd
.required_graph();
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
)
365 ArgPredicate
::IsPresent
=> true,
367 required
.then(|| req_arg
.clone())
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
);
375 // always include the required arg itself. it will not be enumerated
376 // by unroll_requirements_for_arg.
377 unrolled_reqs
.insert(a
.clone());
381 "Usage::get_required_usage_from: unrolled_reqs={:?}",
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
))
395 "Usage::get_required_usage_from:iter:{:?} arg 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()));
404 required_opts
.insert(arg
.to_string());
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
414 .any(|arg
| m
.check_explicit(arg
, ArgPredicate
::IsPresent
))
418 "Usage::get_required_usage_from:iter:{:?} group is_present={}",
422 let elem
= self.cmd
.format_group(req
);
423 required_groups
.insert(elem
);
424 required_groups_members
.extend(
427 .flat_map(|id
| self.cmd
.find(id
))
428 .map(|arg
| arg
.to_string()),
434 required_opts
.retain(|arg
| !required_groups_members
.contains(arg
));
435 ret_val
.extend(required_opts
);
437 ret_val
.extend(required_groups
);
439 required_positionals
.sort_by_key(|(ind
, _
)| *ind
); // sort by index
440 for (_
, p
) in required_positionals
{
441 if !required_groups_members
.contains(&p
) {
446 debug
!("Usage::get_required_usage_from: ret_val={:?}", ret_val
);