1use std::collections::{BTreeMap, VecDeque};
3
4use crate::{
6 app::{parser::Parser, settings::AppSettings as AS},
7 args::{settings::ArgSettings, AnyArg, ArgMatcher, PosBuilder},
8 INTERNAL_ERROR_MSG,
9};
10
11pub 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
21pub 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 }
38 })
39 .copied()
40 .collect();
41 if let Some(r) = extra {
42 args.push(r);
43 }
44 create_usage_with_title(p, &*args)
45}
46
47pub 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
59pub 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_unstable();
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 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('>');
138 usage.push_str(pos.multiple_str());
139 if !req {
140 usage.push(']');
141 }
142 }
143 }
144
145 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 usage.push_str("\n ");
149 if !p.is_set(AS::ArgsNegateSubcommands) {
150 usage.push_str(&*create_help_usage(p, false));
151 } else {
152 usage.push_str(&*name);
153 }
154 usage.push_str(" <SUBCOMMAND>");
155 } else if p.is_set(AS::SubcommandRequired) || p.is_set(AS::SubcommandRequiredElseHelp) {
156 usage.push_str(" <SUBCOMMAND>");
157 } else {
158 usage.push_str(" [SUBCOMMAND]");
159 }
160 }
161 usage.shrink_to_fit();
162 debugln!("usage::create_help_usage: usage={}", usage);
163 usage
164}
165
166fn create_smart_usage(p: &Parser, used: &[&str]) -> String {
169 debugln!("usage::smart_usage;");
170 let mut usage = String::with_capacity(75);
171 let mut hs: Vec<&str> = p.required().map(|s| &**s).collect();
172 hs.extend_from_slice(used);
173
174 let r_string = get_required_usage_from(p, &hs, None, None, false)
175 .iter()
176 .fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
177
178 usage.push_str(
179 &p.meta
180 .usage
181 .as_ref()
182 .unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name))[..],
183 );
184 usage.push_str(&*r_string);
185 if p.is_set(AS::SubcommandRequired) {
186 usage.push_str(" <SUBCOMMAND>");
187 }
188 usage.shrink_to_fit();
189 usage
190}
191
192fn get_args_tag(p: &Parser, incl_reqs: bool) -> Option<String> {
194 debugln!("usage::get_args_tag;");
195 let mut count = 0;
196 'outer: for pos in p
197 .positionals
198 .values()
199 .filter(|pos| !pos.is_set(ArgSettings::Required))
200 .filter(|pos| !pos.is_set(ArgSettings::Hidden))
201 .filter(|pos| !pos.is_set(ArgSettings::Last))
202 {
203 debugln!("usage::get_args_tag:iter:{}:", pos.b.name);
204 if let Some(g_vec) = p.groups_for_arg(pos.b.name) {
205 for grp_s in &g_vec {
206 debugln!("usage::get_args_tag:iter:{}:iter:{};", pos.b.name, grp_s);
207 if p.groups.iter().any(|g| g.required && (&g.name == grp_s)) {
209 continue 'outer;
210 }
211 }
212 }
213 count += 1;
214 debugln!(
215 "usage::get_args_tag:iter: {} Args not required or hidden",
216 count
217 );
218 }
219 if !p.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
220 debugln!("usage::get_args_tag:iter: More than one, returning [ARGS]");
221 return None; } else if count == 1 && incl_reqs {
223 let pos = p
224 .positionals
225 .values()
226 .find(|pos| {
227 !pos.is_set(ArgSettings::Required)
228 && !pos.is_set(ArgSettings::Hidden)
229 && !pos.is_set(ArgSettings::Last)
230 })
231 .expect(INTERNAL_ERROR_MSG);
232 debugln!(
233 "usage::get_args_tag:iter: Exactly one, returning '{}'",
234 pos.name()
235 );
236 return Some(format!(
237 " [{}]{}",
238 pos.name_no_brackets(),
239 pos.multiple_str()
240 ));
241 } else if p.is_set(AS::DontCollapseArgsInUsage) && !p.positionals.is_empty() && incl_reqs {
242 debugln!("usage::get_args_tag:iter: Don't collapse returning all");
243 return Some(
244 p.positionals
245 .values()
246 .filter(|pos| !pos.is_set(ArgSettings::Required))
247 .filter(|pos| !pos.is_set(ArgSettings::Hidden))
248 .filter(|pos| !pos.is_set(ArgSettings::Last))
249 .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
250 .collect::<Vec<_>>()
251 .join(""),
252 );
253 } else if !incl_reqs {
254 debugln!("usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
255 let highest_req_pos = p
256 .positionals
257 .iter()
258 .filter_map(|(idx, pos)| {
259 if pos.b.is_set(ArgSettings::Required) && !pos.b.is_set(ArgSettings::Last) {
260 Some(idx)
261 } else {
262 None
263 }
264 })
265 .max()
266 .unwrap_or_else(|| p.positionals.len());
267 return Some(
268 p.positionals
269 .iter()
270 .filter_map(|(idx, pos)| {
271 if idx <= highest_req_pos {
272 Some(pos)
273 } else {
274 None
275 }
276 })
277 .filter(|pos| !pos.is_set(ArgSettings::Required))
278 .filter(|pos| !pos.is_set(ArgSettings::Hidden))
279 .filter(|pos| !pos.is_set(ArgSettings::Last))
280 .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
281 .collect::<Vec<_>>()
282 .join(""),
283 );
284 }
285 Some("".into())
286}
287
288fn needs_flags_tag(p: &Parser) -> bool {
290 debugln!("usage::needs_flags_tag;");
291 'outer: for f in &p.flags {
292 debugln!("usage::needs_flags_tag:iter: f={};", f.b.name);
293 if let Some(l) = f.s.long {
294 if l == "help" || l == "version" {
295 continue;
297 }
298 }
299 if let Some(g_vec) = p.groups_for_arg(f.b.name) {
300 for grp_s in &g_vec {
301 debugln!("usage::needs_flags_tag:iter:iter: grp_s={};", grp_s);
302 if p.groups.iter().any(|g| &g.name == grp_s && g.required) {
303 debugln!("usage::needs_flags_tag:iter:iter: Group is required");
304 continue 'outer;
305 }
306 }
307 }
308 if f.is_set(ArgSettings::Hidden) {
309 continue;
310 }
311 debugln!("usage::needs_flags_tag:iter: [FLAGS] required");
312 return true;
313 }
314
315 debugln!("usage::needs_flags_tag: [FLAGS] not required");
316 false
317}
318
319pub fn get_required_usage_from<'a, 'b>(
321 p: &Parser<'a, 'b>,
322 reqs: &[&'a str],
323 matcher: Option<&ArgMatcher<'a>>,
324 extra: Option<&str>,
325 incl_last: bool,
326) -> VecDeque<String> {
327 debugln!(
328 "usage::get_required_usage_from: reqs={:?}, extra={:?}",
329 reqs,
330 extra
331 );
332 let mut desc_reqs: Vec<&str> = vec![];
333 desc_reqs.extend(extra);
334 let mut new_reqs: Vec<&str> = vec![];
335 macro_rules! get_requires {
336 (@group $a: ident, $v:ident, $p:ident) => {{
337 if let Some(rl) = p
338 .groups
339 .iter()
340 .filter(|g| g.requires.is_some())
341 .find(|g| &g.name == $a)
342 .map(|g| g.requires.as_ref().unwrap())
343 {
344 for r in rl {
345 if !$p.contains(&r) {
346 debugln!(
347 "usage::get_required_usage_from:iter:{}: adding group req={:?}",
348 $a,
349 r
350 );
351 $v.push(r);
352 }
353 }
354 }
355 }};
356 ($a:ident, $what:ident, $how:ident, $v:ident, $p:ident) => {{
357 if let Some(rl) = p
358 .$what
359 .$how()
360 .filter(|a| a.b.requires.is_some())
361 .find(|arg| &arg.b.name == $a)
362 .map(|a| a.b.requires.as_ref().unwrap())
363 {
364 for &(_, r) in rl.iter() {
365 if !$p.contains(&r) {
366 debugln!(
367 "usage::get_required_usage_from:iter:{}: adding arg req={:?}",
368 $a,
369 r
370 );
371 $v.push(r);
372 }
373 }
374 }
375 }};
376 }
377 for a in reqs {
379 get_requires!(a, flags, iter, new_reqs, reqs);
380 get_requires!(a, opts, iter, new_reqs, reqs);
381 get_requires!(a, positionals, values, new_reqs, reqs);
382 get_requires!(@group a, new_reqs, reqs);
383 }
384 desc_reqs.extend_from_slice(&*new_reqs);
385 debugln!(
386 "usage::get_required_usage_from: after init desc_reqs={:?}",
387 desc_reqs
388 );
389 loop {
390 let mut tmp = vec![];
391 for a in &new_reqs {
392 get_requires!(a, flags, iter, tmp, desc_reqs);
393 get_requires!(a, opts, iter, tmp, desc_reqs);
394 get_requires!(a, positionals, values, tmp, desc_reqs);
395 get_requires!(@group a, tmp, desc_reqs);
396 }
397 if tmp.is_empty() {
398 debugln!("usage::get_required_usage_from: no more children");
399 break;
400 } else {
401 debugln!("usage::get_required_usage_from: after iter tmp={:?}", tmp);
402 debugln!(
403 "usage::get_required_usage_from: after iter new_reqs={:?}",
404 new_reqs
405 );
406 desc_reqs.extend_from_slice(&*new_reqs);
407 new_reqs.clear();
408 new_reqs.extend_from_slice(&*tmp);
409 debugln!(
410 "usage::get_required_usage_from: after iter desc_reqs={:?}",
411 desc_reqs
412 );
413 }
414 }
415 desc_reqs.extend_from_slice(reqs);
416 desc_reqs.sort_unstable();
417 desc_reqs.dedup();
418 debugln!(
419 "usage::get_required_usage_from: final desc_reqs={:?}",
420 desc_reqs
421 );
422 let mut ret_val = VecDeque::new();
423 let args_in_groups = p
424 .groups
425 .iter()
426 .filter(|gn| desc_reqs.contains(&gn.name))
427 .flat_map(|g| p.arg_names_in_group(g.name))
428 .collect::<Vec<_>>();
429
430 let pmap = if let Some(m) = matcher {
431 desc_reqs
432 .iter()
433 .filter(|a| p.positionals.values().any(|p| &&p.b.name == a))
434 .filter(|&pos| !m.contains(pos))
435 .filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos))
436 .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
437 .filter(|pos| !args_in_groups.contains(&pos.b.name))
438 .map(|pos| (pos.index, pos))
439 .collect::<BTreeMap<u64, &PosBuilder>>() } else {
441 desc_reqs
442 .iter()
443 .filter(|a| p.positionals.values().any(|pos| &&pos.b.name == a))
444 .filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos))
445 .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
446 .filter(|pos| !args_in_groups.contains(&pos.b.name))
447 .map(|pos| (pos.index, pos))
448 .collect::<BTreeMap<u64, &PosBuilder>>() };
450 debugln!(
451 "usage::get_required_usage_from: args_in_groups={:?}",
452 args_in_groups
453 );
454 for &p in pmap.values() {
455 let s = p.to_string();
456 if args_in_groups.is_empty() || !args_in_groups.contains(&&*s) {
457 ret_val.push_back(s);
458 }
459 }
460 for a in desc_reqs
461 .iter()
462 .filter(|name| !p.positionals.values().any(|p| &&p.b.name == name))
463 .filter(|name| !p.groups.iter().any(|g| &&g.name == name))
464 .filter(|name| !args_in_groups.contains(name))
465 .filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name)))
466 {
467 debugln!("usage::get_required_usage_from:iter:{}:", a);
468 let arg = find_by_name!(p, *a, flags, iter)
469 .map(|f| f.to_string())
470 .unwrap_or_else(|| {
471 find_by_name!(p, *a, opts, iter)
472 .map(|o| o.to_string())
473 .expect(INTERNAL_ERROR_MSG)
474 });
475 ret_val.push_back(arg);
476 }
477 let mut g_vec: Vec<String> = vec![];
478 for g in desc_reqs
479 .iter()
480 .filter(|n| p.groups.iter().any(|g| &&g.name == n))
481 {
482 let g_string = p.args_in_group(g).join("|");
483 let elem = format!("<{}>", &g_string[..g_string.len()]);
484 if !g_vec.contains(&elem) {
485 g_vec.push(elem);
486 }
487 }
488 for g in g_vec {
489 ret_val.push_back(g);
490 }
491
492 ret_val
493}