clap/app/
usage.rs

1// std
2use std::collections::{BTreeMap, VecDeque};
3
4// Internal
5use INTERNAL_ERROR_MSG;
6use args::{AnyArg, ArgMatcher, PosBuilder};
7use args::settings::ArgSettings;
8use app::settings::AppSettings as AS;
9use 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)
13pub 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)
22pub 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.
48pub 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)
60pub 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
166fn 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
191fn 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
287fn 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
318pub 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}