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