1 // std
2 use std::collections::{BTreeMap, VecDeque};
3 
4 // Internal
5 use INTERNAL_ERROR_MSG;
6 use args::{AnyArg, ArgMatcher, PosBuilder};
7 use args::settings::ArgSettings;
8 use app::settings::AppSettings as AS;
9 use app::parser::Parser;
10 
11 // Creates a usage string for display. This happens just after all arguments were parsed, but before
12 // any subcommands have been parsed (so as to give subcommands their own usage recursively)
create_usage_with_title(p: &Parser, used: &[&str]) -> String13 pub fn create_usage_with_title(p: &Parser, used: &[&str]) -> String {
14     debugln!("usage::create_usage_with_title;");
15     let mut usage = String::with_capacity(75);
16     usage.push_str("USAGE:\n    ");
17     usage.push_str(&*create_usage_no_title(p, used));
18     usage
19 }
20 
21 // Creates a usage string to be used in error message (i.e. one with currently used args)
create_error_usage<'a, 'b>( p: &Parser<'a, 'b>, matcher: &'b ArgMatcher<'a>, extra: Option<&str>, ) -> String22 pub fn create_error_usage<'a, 'b>(
23     p: &Parser<'a, 'b>,
24     matcher: &'b ArgMatcher<'a>,
25     extra: Option<&str>,
26 ) -> String {
27     let mut args: Vec<_> = matcher
28         .arg_names()
29         .iter()
30         .filter(|n| {
31             if let Some(o) = find_by_name!(p, **n, opts, iter) {
32                 !o.b.is_set(ArgSettings::Required) && !o.b.is_set(ArgSettings::Hidden)
33             } else if let Some(p) = find_by_name!(p, **n, positionals, values) {
34                 !p.b.is_set(ArgSettings::Required) && p.b.is_set(ArgSettings::Hidden)
35             } else {
36                 true // flags can't be required, so they're always true
37             }
38         })
39         .map(|&n| n)
40         .collect();
41     if let Some(r) = extra {
42         args.push(r);
43     }
44     create_usage_with_title(p, &*args)
45 }
46 
47 // Creates a usage string (*without title*) if one was not provided by the user manually.
create_usage_no_title(p: &Parser, used: &[&str]) -> String48 pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
49     debugln!("usage::create_usage_no_title;");
50     if let Some(u) = p.meta.usage_str {
51         String::from(&*u)
52     } else if used.is_empty() {
53         create_help_usage(p, true)
54     } else {
55         create_smart_usage(p, used)
56     }
57 }
58 
59 // Creates a usage string for display in help messages (i.e. not for errors)
create_help_usage(p: &Parser, incl_reqs: bool) -> String60 pub fn create_help_usage(p: &Parser, incl_reqs: bool) -> String {
61     let mut usage = String::with_capacity(75);
62     let name = p.meta
63         .usage
64         .as_ref()
65         .unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name));
66     usage.push_str(&*name);
67     let req_string = if incl_reqs {
68         let mut reqs: Vec<&str> = p.required().map(|r| &**r).collect();
69         reqs.sort();
70         reqs.dedup();
71         get_required_usage_from(p, &reqs, None, None, false)
72             .iter()
73             .fold(String::new(), |a, s| a + &format!(" {}", s)[..])
74     } else {
75         String::new()
76     };
77 
78     let flags = needs_flags_tag(p);
79     if flags && !p.is_set(AS::UnifiedHelpMessage) {
80         usage.push_str(" [FLAGS]");
81     } else if flags {
82         usage.push_str(" [OPTIONS]");
83     }
84     if !p.is_set(AS::UnifiedHelpMessage) && p.opts.iter().any(|o| {
85         !o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden)
86     }) {
87         usage.push_str(" [OPTIONS]");
88     }
89 
90     usage.push_str(&req_string[..]);
91 
92     let has_last = p.positionals.values().any(|p| p.is_set(ArgSettings::Last));
93     // places a '--' in the usage string if there are args and options
94     // supporting multiple values
95     if p.opts.iter().any(|o| o.is_set(ArgSettings::Multiple))
96         && p.positionals
97             .values()
98             .any(|p| !p.is_set(ArgSettings::Required))
99         && !(p.has_visible_subcommands() || p.is_set(AS::AllowExternalSubcommands))
100         && !has_last
101     {
102         usage.push_str(" [--]");
103     }
104     let not_req_or_hidden = |p: &PosBuilder| {
105         (!p.is_set(ArgSettings::Required) || p.is_set(ArgSettings::Last))
106             && !p.is_set(ArgSettings::Hidden)
107     };
108     if p.has_positionals() && p.positionals.values().any(not_req_or_hidden) {
109         if let Some(args_tag) = get_args_tag(p, incl_reqs) {
110             usage.push_str(&*args_tag);
111         } else {
112             usage.push_str(" [ARGS]");
113         }
114         if has_last && incl_reqs {
115             let pos = p.positionals
116                 .values()
117                 .find(|p| p.b.is_set(ArgSettings::Last))
118                 .expect(INTERNAL_ERROR_MSG);
119             debugln!("usage::create_help_usage: '{}' has .last(true)", pos.name());
120             let req = pos.is_set(ArgSettings::Required);
121             if req
122                 && p.positionals
123                     .values()
124                     .any(|p| !p.is_set(ArgSettings::Required))
125             {
126                 usage.push_str(" -- <");
127             } else if req {
128                 usage.push_str(" [--] <");
129             } else {
130                 usage.push_str(" [-- <");
131             }
132             usage.push_str(&*pos.name_no_brackets());
133             usage.push_str(">");
134             usage.push_str(pos.multiple_str());
135             if !req {
136                 usage.push_str("]");
137             }
138         }
139     }
140 
141     // incl_reqs is only false when this function is called recursively
142     if p.has_visible_subcommands() && incl_reqs || p.is_set(AS::AllowExternalSubcommands) {
143         if p.is_set(AS::SubcommandsNegateReqs) || p.is_set(AS::ArgsNegateSubcommands) {
144             if !p.is_set(AS::ArgsNegateSubcommands) {
145                 usage.push_str("\n    ");
146                 usage.push_str(&*create_help_usage(p, false));
147                 usage.push_str(" <SUBCOMMAND>");
148             } else {
149                 usage.push_str("\n    ");
150                 usage.push_str(&*name);
151                 usage.push_str(" <SUBCOMMAND>");
152             }
153         } else if p.is_set(AS::SubcommandRequired) || p.is_set(AS::SubcommandRequiredElseHelp) {
154             usage.push_str(" <SUBCOMMAND>");
155         } else {
156             usage.push_str(" [SUBCOMMAND]");
157         }
158     }
159     usage.shrink_to_fit();
160     debugln!("usage::create_help_usage: usage={}", usage);
161     usage
162 }
163 
164 // Creates a context aware usage string, or "smart usage" from currently used
165 // args, and requirements
create_smart_usage(p: &Parser, used: &[&str]) -> String166 fn create_smart_usage(p: &Parser, used: &[&str]) -> String {
167     debugln!("usage::smart_usage;");
168     let mut usage = String::with_capacity(75);
169     let mut hs: Vec<&str> = p.required().map(|s| &**s).collect();
170     hs.extend_from_slice(used);
171 
172     let r_string = get_required_usage_from(p, &hs, None, None, false)
173         .iter()
174         .fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
175 
176     usage.push_str(
177         &p.meta
178             .usage
179             .as_ref()
180             .unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name))[..],
181     );
182     usage.push_str(&*r_string);
183     if p.is_set(AS::SubcommandRequired) {
184         usage.push_str(" <SUBCOMMAND>");
185     }
186     usage.shrink_to_fit();
187     usage
188 }
189 
190 // Gets the `[ARGS]` tag for the usage string
get_args_tag(p: &Parser, incl_reqs: bool) -> Option<String>191 fn get_args_tag(p: &Parser, incl_reqs: bool) -> Option<String> {
192     debugln!("usage::get_args_tag;");
193     let mut count = 0;
194     'outer: for pos in p.positionals
195         .values()
196         .filter(|pos| !pos.is_set(ArgSettings::Required))
197         .filter(|pos| !pos.is_set(ArgSettings::Hidden))
198         .filter(|pos| !pos.is_set(ArgSettings::Last))
199     {
200         debugln!("usage::get_args_tag:iter:{}:", pos.b.name);
201         if let Some(g_vec) = p.groups_for_arg(pos.b.name) {
202             for grp_s in &g_vec {
203                 debugln!("usage::get_args_tag:iter:{}:iter:{};", pos.b.name, grp_s);
204                 // if it's part of a required group we don't want to count it
205                 if p.groups.iter().any(|g| g.required && (&g.name == grp_s)) {
206                     continue 'outer;
207                 }
208             }
209         }
210         count += 1;
211         debugln!(
212             "usage::get_args_tag:iter: {} Args not required or hidden",
213             count
214         );
215     }
216     if !p.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
217         debugln!("usage::get_args_tag:iter: More than one, returning [ARGS]");
218         return None; // [ARGS]
219     } else if count == 1 && incl_reqs {
220         let pos = p.positionals
221             .values()
222             .find(|pos| {
223                 !pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Hidden)
224                     && !pos.is_set(ArgSettings::Last)
225             })
226             .expect(INTERNAL_ERROR_MSG);
227         debugln!(
228             "usage::get_args_tag:iter: Exactly one, returning '{}'",
229             pos.name()
230         );
231         return Some(format!(
232             " [{}]{}",
233             pos.name_no_brackets(),
234             pos.multiple_str()
235         ));
236     } else if p.is_set(AS::DontCollapseArgsInUsage) && !p.positionals.is_empty() && incl_reqs {
237         debugln!("usage::get_args_tag:iter: Don't collapse returning all");
238         return Some(
239             p.positionals
240                 .values()
241                 .filter(|pos| !pos.is_set(ArgSettings::Required))
242                 .filter(|pos| !pos.is_set(ArgSettings::Hidden))
243                 .filter(|pos| !pos.is_set(ArgSettings::Last))
244                 .map(|pos| {
245                     format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str())
246                 })
247                 .collect::<Vec<_>>()
248                 .join(""),
249         );
250     } else if !incl_reqs {
251         debugln!("usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
252         let highest_req_pos = p.positionals
253             .iter()
254             .filter_map(|(idx, pos)| {
255                 if pos.b.is_set(ArgSettings::Required) && !pos.b.is_set(ArgSettings::Last) {
256                     Some(idx)
257                 } else {
258                     None
259                 }
260             })
261             .max()
262             .unwrap_or_else(|| p.positionals.len());
263         return Some(
264             p.positionals
265                 .iter()
266                 .filter_map(|(idx, pos)| {
267                     if idx <= highest_req_pos {
268                         Some(pos)
269                     } else {
270                         None
271                     }
272                 })
273                 .filter(|pos| !pos.is_set(ArgSettings::Required))
274                 .filter(|pos| !pos.is_set(ArgSettings::Hidden))
275                 .filter(|pos| !pos.is_set(ArgSettings::Last))
276                 .map(|pos| {
277                     format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str())
278                 })
279                 .collect::<Vec<_>>()
280                 .join(""),
281         );
282     }
283     Some("".into())
284 }
285 
286 // Determines if we need the `[FLAGS]` tag in the usage string
needs_flags_tag(p: &Parser) -> bool287 fn needs_flags_tag(p: &Parser) -> bool {
288     debugln!("usage::needs_flags_tag;");
289     'outer: for f in &p.flags {
290         debugln!("usage::needs_flags_tag:iter: f={};", f.b.name);
291         if let Some(l) = f.s.long {
292             if l == "help" || l == "version" {
293                 // Don't print `[FLAGS]` just for help or version
294                 continue;
295             }
296         }
297         if let Some(g_vec) = p.groups_for_arg(f.b.name) {
298             for grp_s in &g_vec {
299                 debugln!("usage::needs_flags_tag:iter:iter: grp_s={};", grp_s);
300                 if p.groups.iter().any(|g| &g.name == grp_s && g.required) {
301                     debugln!("usage::needs_flags_tag:iter:iter: Group is required");
302                     continue 'outer;
303                 }
304             }
305         }
306         if f.is_set(ArgSettings::Hidden) {
307             continue;
308         }
309         debugln!("usage::needs_flags_tag:iter: [FLAGS] required");
310         return true;
311     }
312 
313     debugln!("usage::needs_flags_tag: [FLAGS] not required");
314     false
315 }
316 
317 // Returns the required args in usage string form by fully unrolling all groups
get_required_usage_from<'a, 'b>( p: &Parser<'a, 'b>, reqs: &[&'a str], matcher: Option<&ArgMatcher<'a>>, extra: Option<&str>, incl_last: bool, ) -> VecDeque<String>318 pub fn get_required_usage_from<'a, 'b>(
319     p: &Parser<'a, 'b>,
320     reqs: &[&'a str],
321     matcher: Option<&ArgMatcher<'a>>,
322     extra: Option<&str>,
323     incl_last: bool,
324 ) -> VecDeque<String> {
325     debugln!(
326         "usage::get_required_usage_from: reqs={:?}, extra={:?}",
327         reqs,
328         extra
329     );
330     let mut desc_reqs: Vec<&str> = vec![];
331     desc_reqs.extend(extra);
332     let mut new_reqs: Vec<&str> = vec![];
333     macro_rules! get_requires {
334         (@group $a: ident, $v:ident, $p:ident) => {{
335             if let Some(rl) = p.groups.iter()
336                                             .filter(|g| g.requires.is_some())
337                                             .find(|g| &g.name == $a)
338                                             .map(|g| g.requires.as_ref().unwrap()) {
339                 for r in rl {
340                     if !$p.contains(&r) {
341                         debugln!("usage::get_required_usage_from:iter:{}: adding group req={:?}",
342                             $a, r);
343                         $v.push(r);
344                     }
345                 }
346             }
347         }};
348         ($a:ident, $what:ident, $how:ident, $v:ident, $p:ident) => {{
349             if let Some(rl) = p.$what.$how()
350                                         .filter(|a| a.b.requires.is_some())
351                                         .find(|arg| &arg.b.name == $a)
352                                         .map(|a| a.b.requires.as_ref().unwrap()) {
353                 for &(_, r) in rl.iter() {
354                     if !$p.contains(&r) {
355                         debugln!("usage::get_required_usage_from:iter:{}: adding arg req={:?}",
356                             $a, r);
357                         $v.push(r);
358                     }
359                 }
360             }
361         }};
362     }
363     // initialize new_reqs
364     for a in reqs {
365         get_requires!(a, flags, iter, new_reqs, reqs);
366         get_requires!(a, opts, iter, new_reqs, reqs);
367         get_requires!(a, positionals, values, new_reqs, reqs);
368         get_requires!(@group a, new_reqs, reqs);
369     }
370     desc_reqs.extend_from_slice(&*new_reqs);
371     debugln!(
372         "usage::get_required_usage_from: after init desc_reqs={:?}",
373         desc_reqs
374     );
375     loop {
376         let mut tmp = vec![];
377         for a in &new_reqs {
378             get_requires!(a, flags, iter, tmp, desc_reqs);
379             get_requires!(a, opts, iter, tmp, desc_reqs);
380             get_requires!(a, positionals, values, tmp, desc_reqs);
381             get_requires!(@group a, tmp, desc_reqs);
382         }
383         if tmp.is_empty() {
384             debugln!("usage::get_required_usage_from: no more children");
385             break;
386         } else {
387             debugln!("usage::get_required_usage_from: after iter tmp={:?}", tmp);
388             debugln!(
389                 "usage::get_required_usage_from: after iter new_reqs={:?}",
390                 new_reqs
391             );
392             desc_reqs.extend_from_slice(&*new_reqs);
393             new_reqs.clear();
394             new_reqs.extend_from_slice(&*tmp);
395             debugln!(
396                 "usage::get_required_usage_from: after iter desc_reqs={:?}",
397                 desc_reqs
398             );
399         }
400     }
401     desc_reqs.extend_from_slice(reqs);
402     desc_reqs.sort();
403     desc_reqs.dedup();
404     debugln!(
405         "usage::get_required_usage_from: final desc_reqs={:?}",
406         desc_reqs
407     );
408     let mut ret_val = VecDeque::new();
409     let args_in_groups = p.groups
410         .iter()
411         .filter(|gn| desc_reqs.contains(&gn.name))
412         .flat_map(|g| p.arg_names_in_group(g.name))
413         .collect::<Vec<_>>();
414 
415     let pmap = if let Some(m) = matcher {
416         desc_reqs
417             .iter()
418             .filter(|a| p.positionals.values().any(|p| &&p.b.name == a))
419             .filter(|&pos| !m.contains(pos))
420             .filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos))
421             .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
422             .filter(|pos| !args_in_groups.contains(&pos.b.name))
423             .map(|pos| (pos.index, pos))
424             .collect::<BTreeMap<u64, &PosBuilder>>() // sort by index
425     } else {
426         desc_reqs
427             .iter()
428             .filter(|a| p.positionals.values().any(|pos| &&pos.b.name == a))
429             .filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos))
430             .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
431             .filter(|pos| !args_in_groups.contains(&pos.b.name))
432             .map(|pos| (pos.index, pos))
433             .collect::<BTreeMap<u64, &PosBuilder>>() // sort by index
434     };
435     debugln!(
436         "usage::get_required_usage_from: args_in_groups={:?}",
437         args_in_groups
438     );
439     for &p in pmap.values() {
440         let s = p.to_string();
441         if args_in_groups.is_empty() || !args_in_groups.contains(&&*s) {
442             ret_val.push_back(s);
443         }
444     }
445     for a in desc_reqs
446         .iter()
447         .filter(|name| !p.positionals.values().any(|p| &&p.b.name == name))
448         .filter(|name| !p.groups.iter().any(|g| &&g.name == name))
449         .filter(|name| !args_in_groups.contains(name))
450         .filter(|name| {
451             !(matcher.is_some() && matcher.as_ref().unwrap().contains(name))
452         }) {
453         debugln!("usage::get_required_usage_from:iter:{}:", a);
454         let arg = find_by_name!(p, *a, flags, iter)
455             .map(|f| f.to_string())
456             .unwrap_or_else(|| {
457                 find_by_name!(p, *a, opts, iter)
458                     .map(|o| o.to_string())
459                     .expect(INTERNAL_ERROR_MSG)
460             });
461         ret_val.push_back(arg);
462     }
463     let mut g_vec: Vec<String> = vec![];
464     for g in desc_reqs
465         .iter()
466         .filter(|n| p.groups.iter().any(|g| &&g.name == n))
467     {
468         let g_string = p.args_in_group(g).join("|");
469         let elem = format!("<{}>", &g_string[..g_string.len()]);
470         if !g_vec.contains(&elem) {
471             g_vec.push(elem);
472         }
473     }
474     for g in g_vec {
475         ret_val.push_back(g);
476     }
477 
478     ret_val
479 }
480