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