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