]> git.proxmox.com Git - rustc.git/blob - src/vendor/clap/src/app/help.rs
New upstream version 1.29.0+dfsg1
[rustc.git] / src / vendor / clap / src / app / help.rs
1 // Std
2 use std::borrow::Cow;
3 use std::cmp;
4 use std::collections::BTreeMap;
5 use std::fmt::Display;
6 use std::io::{self, Cursor, Read, Write};
7 use std::usize;
8
9 // Internal
10 use app::parser::Parser;
11 use app::usage;
12 use app::{App, AppSettings};
13 use args::{AnyArg, ArgSettings, DispOrder};
14 use errors::{Error, Result as ClapResult};
15 use fmt::{Colorizer, ColorizerOption, Format};
16 use map::VecMap;
17 use INTERNAL_ERROR_MSG;
18
19 // Third Party
20 #[cfg(feature = "wrap_help")]
21 use term_size;
22 use textwrap;
23 use unicode_width::UnicodeWidthStr;
24
25 #[cfg(not(feature = "wrap_help"))]
26 mod term_size {
27 pub fn dimensions() -> Option<(usize, usize)> {
28 None
29 }
30 }
31
32 fn str_width(s: &str) -> usize {
33 UnicodeWidthStr::width(s)
34 }
35
36 const TAB: &'static str = " ";
37
38 // These are just convenient traits to make the code easier to read.
39 trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
40 impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T
41 where
42 T: AnyArg<'b, 'c> + Display,
43 {
44 }
45
46 trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
47 fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
48 }
49 impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
50 where
51 T: ArgWithDisplay<'b, 'c> + DispOrder,
52 {
53 fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
54 self
55 }
56 }
57
58 fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
59 x
60 }
61
62 impl<'b, 'c> DispOrder for App<'b, 'c> {
63 fn disp_ord(&self) -> usize {
64 999
65 }
66 }
67
68 macro_rules! color {
69 ($_self:ident, $s:expr, $c:ident) => {
70 if $_self.color {
71 write!($_self.writer, "{}", $_self.cizer.$c($s))
72 } else {
73 write!($_self.writer, "{}", $s)
74 }
75 };
76 ($_self:ident, $fmt_s:expr, $v:expr, $c:ident) => {
77 if $_self.color {
78 write!($_self.writer, "{}", $_self.cizer.$c(format!($fmt_s, $v)))
79 } else {
80 write!($_self.writer, $fmt_s, $v)
81 }
82 };
83 }
84
85 /// `clap` Help Writer.
86 ///
87 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
88 pub struct Help<'a> {
89 writer: &'a mut Write,
90 next_line_help: bool,
91 hide_pv: bool,
92 term_w: usize,
93 color: bool,
94 cizer: Colorizer,
95 longest: usize,
96 force_next_line: bool,
97 use_long: bool,
98 }
99
100 // Public Functions
101 impl<'a> Help<'a> {
102 /// Create a new `Help` instance.
103 #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
104 pub fn new(
105 w: &'a mut Write,
106 next_line_help: bool,
107 hide_pv: bool,
108 color: bool,
109 cizer: Colorizer,
110 term_w: Option<usize>,
111 max_w: Option<usize>,
112 use_long: bool,
113 ) -> Self {
114 debugln!("Help::new;");
115 Help {
116 writer: w,
117 next_line_help: next_line_help,
118 hide_pv: hide_pv,
119 term_w: match term_w {
120 Some(width) => if width == 0 {
121 usize::MAX
122 } else {
123 width
124 },
125 None => cmp::min(
126 term_size::dimensions().map_or(120, |(w, _)| w),
127 match max_w {
128 None | Some(0) => usize::MAX,
129 Some(mw) => mw,
130 },
131 ),
132 },
133 color: color,
134 cizer: cizer,
135 longest: 0,
136 force_next_line: false,
137 use_long: use_long,
138 }
139 }
140
141 /// Reads help settings from an App
142 /// and write its help to the wrapped stream.
143 pub fn write_app_help(w: &'a mut Write, app: &App, use_long: bool) -> ClapResult<()> {
144 debugln!("Help::write_app_help;");
145 Self::write_parser_help(w, &app.p, use_long)
146 }
147
148 /// Reads help settings from a Parser
149 /// and write its help to the wrapped stream.
150 pub fn write_parser_help(w: &'a mut Write, parser: &Parser, use_long: bool) -> ClapResult<()> {
151 debugln!("Help::write_parser_help;");
152 Self::_write_parser_help(w, parser, false, use_long)
153 }
154
155 /// Reads help settings from a Parser
156 /// and write its help to the wrapped stream which will be stderr. This method prevents
157 /// formatting when required.
158 pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
159 debugln!("Help::write_parser_help;");
160 Self::_write_parser_help(w, parser, true, false)
161 }
162
163 #[doc(hidden)]
164 pub fn _write_parser_help(
165 w: &'a mut Write,
166 parser: &Parser,
167 stderr: bool,
168 use_long: bool,
169 ) -> ClapResult<()> {
170 debugln!("Help::write_parser_help;");
171 let nlh = parser.is_set(AppSettings::NextLineHelp);
172 let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
173 let color = parser.is_set(AppSettings::ColoredHelp);
174 let cizer = Colorizer::new(ColorizerOption {
175 use_stderr: stderr,
176 when: parser.color(),
177 });
178 Self::new(
179 w,
180 nlh,
181 hide_v,
182 color,
183 cizer,
184 parser.meta.term_w,
185 parser.meta.max_w,
186 use_long,
187 ).write_help(parser)
188 }
189
190 /// Writes the parser help to the wrapped stream.
191 pub fn write_help(&mut self, parser: &Parser) -> ClapResult<()> {
192 debugln!("Help::write_help;");
193 if let Some(h) = parser.meta.help_str {
194 write!(self.writer, "{}", h).map_err(Error::from)?;
195 } else if let Some(tmpl) = parser.meta.template {
196 self.write_templated_help(parser, tmpl)?;
197 } else {
198 self.write_default_help(parser)?;
199 }
200 Ok(())
201 }
202 }
203
204 // Methods to write AnyArg help.
205 impl<'a> Help<'a> {
206 /// Writes help for each argument in the order they were declared to the wrapped stream.
207 fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
208 where
209 I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>,
210 {
211 debugln!("Help::write_args_unsorted;");
212 // The shortest an arg can legally be is 2 (i.e. '-x')
213 self.longest = 2;
214 let mut arg_v = Vec::with_capacity(10);
215 let use_long = self.use_long;
216 for arg in args.filter(|arg| should_show_arg(use_long, *arg)) {
217 if arg.longest_filter() {
218 self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
219 }
220 arg_v.push(arg)
221 }
222 let mut first = true;
223 for arg in arg_v {
224 if first {
225 first = false;
226 } else {
227 self.writer.write_all(b"\n")?;
228 }
229 self.write_arg(arg.as_base())?;
230 }
231 Ok(())
232 }
233
234 /// Sorts arguments by length and display order and write their help to the wrapped stream.
235 fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
236 where
237 I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>,
238 {
239 debugln!("Help::write_args;");
240 // The shortest an arg can legally be is 2 (i.e. '-x')
241 self.longest = 2;
242 let mut ord_m = VecMap::new();
243 let use_long = self.use_long;
244 // Determine the longest
245 for arg in args.filter(|arg| {
246 // If it's NextLineHelp, but we don't care to compute how long because it may be
247 // NextLineHelp on purpose *because* it's so long and would throw off all other
248 // args alignment
249 should_show_arg(use_long, *arg)
250 }) {
251 if arg.longest_filter() {
252 debugln!("Help::write_args: Current Longest...{}", self.longest);
253 self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
254 debugln!("Help::write_args: New Longest...{}", self.longest);
255 }
256 let btm = ord_m.entry(arg.disp_ord()).or_insert(BTreeMap::new());
257 btm.insert(arg.name(), arg);
258 }
259 let mut first = true;
260 for btm in ord_m.values() {
261 for arg in btm.values() {
262 if first {
263 first = false;
264 } else {
265 self.writer.write_all(b"\n")?;
266 }
267 self.write_arg(arg.as_base())?;
268 }
269 }
270 Ok(())
271 }
272
273 /// Writes help for an argument to the wrapped stream.
274 fn write_arg<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
275 debugln!("Help::write_arg;");
276 self.short(arg)?;
277 self.long(arg)?;
278 let spec_vals = self.val(arg)?;
279 self.help(arg, &*spec_vals)?;
280 Ok(())
281 }
282
283 /// Writes argument's short command to the wrapped stream.
284 fn short<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
285 debugln!("Help::short;");
286 write!(self.writer, "{}", TAB)?;
287 if let Some(s) = arg.short() {
288 color!(self, "-{}", s, good)
289 } else if arg.has_switch() {
290 write!(self.writer, "{}", TAB)
291 } else {
292 Ok(())
293 }
294 }
295
296 /// Writes argument's long command to the wrapped stream.
297 fn long<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
298 debugln!("Help::long;");
299 if !arg.has_switch() {
300 return Ok(());
301 }
302 if arg.takes_value() {
303 if let Some(l) = arg.long() {
304 if arg.short().is_some() {
305 write!(self.writer, ", ")?;
306 }
307 color!(self, "--{}", l, good)?
308 }
309
310 let sep = if arg.is_set(ArgSettings::RequireEquals) {
311 "="
312 } else {
313 " "
314 };
315 write!(self.writer, "{}", sep)?;
316 } else if let Some(l) = arg.long() {
317 if arg.short().is_some() {
318 write!(self.writer, ", ")?;
319 }
320 color!(self, "--{}", l, good)?;
321 }
322 Ok(())
323 }
324
325 /// Writes argument's possible values to the wrapped stream.
326 fn val<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> Result<String, io::Error> {
327 debugln!("Help::val: arg={}", arg);
328 if arg.takes_value() {
329 let delim = if arg.is_set(ArgSettings::RequireDelimiter) {
330 arg.val_delim().expect(INTERNAL_ERROR_MSG)
331 } else {
332 ' '
333 };
334 if let Some(vec) = arg.val_names() {
335 let mut it = vec.iter().peekable();
336 while let Some((_, val)) = it.next() {
337 color!(self, "<{}>", val, good)?;
338 if it.peek().is_some() {
339 write!(self.writer, "{}", delim)?;
340 }
341 }
342 let num = vec.len();
343 if arg.is_set(ArgSettings::Multiple) && num == 1 {
344 color!(self, "...", good)?;
345 }
346 } else if let Some(num) = arg.num_vals() {
347 let mut it = (0..num).peekable();
348 while let Some(_) = it.next() {
349 color!(self, "<{}>", arg.name(), good)?;
350 if it.peek().is_some() {
351 write!(self.writer, "{}", delim)?;
352 }
353 }
354 if arg.is_set(ArgSettings::Multiple) && num == 1 {
355 color!(self, "...", good)?;
356 }
357 } else if arg.has_switch() {
358 color!(self, "<{}>", arg.name(), good)?;
359 if arg.is_set(ArgSettings::Multiple) {
360 color!(self, "...", good)?;
361 }
362 } else {
363 color!(self, "{}", arg, good)?;
364 }
365 }
366
367 let spec_vals = self.spec_vals(arg);
368 let h = arg.help().unwrap_or("");
369 let h_w = str_width(h) + str_width(&*spec_vals);
370 let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
371 let taken = self.longest + 12;
372 self.force_next_line = !nlh && self.term_w >= taken
373 && (taken as f32 / self.term_w as f32) > 0.40
374 && h_w > (self.term_w - taken);
375
376 debug!("Help::val: Has switch...");
377 if arg.has_switch() {
378 sdebugln!("Yes");
379 debugln!("Help::val: force_next_line...{:?}", self.force_next_line);
380 debugln!("Help::val: nlh...{:?}", nlh);
381 debugln!("Help::val: taken...{}", taken);
382 debugln!(
383 "Help::val: help_width > (width - taken)...{} > ({} - {})",
384 h_w,
385 self.term_w,
386 taken
387 );
388 debugln!("Help::val: longest...{}", self.longest);
389 debug!("Help::val: next_line...");
390 if !(nlh || self.force_next_line) {
391 sdebugln!("No");
392 let self_len = str_width(arg.to_string().as_str());
393 // subtract ourself
394 let mut spcs = self.longest - self_len;
395 // Since we're writing spaces from the tab point we first need to know if we
396 // had a long and short, or just short
397 if arg.long().is_some() {
398 // Only account 4 after the val
399 spcs += 4;
400 } else {
401 // Only account for ', --' + 4 after the val
402 spcs += 8;
403 }
404
405 write_nspaces!(self.writer, spcs);
406 } else {
407 sdebugln!("Yes");
408 }
409 } else if !(nlh || self.force_next_line) {
410 sdebugln!("No, and not next_line");
411 write_nspaces!(
412 self.writer,
413 self.longest + 4 - (str_width(arg.to_string().as_str()))
414 );
415 } else {
416 sdebugln!("No");
417 }
418 Ok(spec_vals)
419 }
420
421 fn write_before_after_help(&mut self, h: &str) -> io::Result<()> {
422 debugln!("Help::write_before_after_help;");
423 let mut help = String::from(h);
424 // determine if our help fits or needs to wrap
425 debugln!(
426 "Help::write_before_after_help: Term width...{}",
427 self.term_w
428 );
429 let too_long = str_width(h) >= self.term_w;
430
431 debug!("Help::write_before_after_help: Too long...");
432 if too_long || h.contains("{n}") {
433 sdebugln!("Yes");
434 debugln!("Help::write_before_after_help: help: {}", help);
435 debugln!(
436 "Help::write_before_after_help: help width: {}",
437 str_width(&*help)
438 );
439 // Determine how many newlines we need to insert
440 debugln!(
441 "Help::write_before_after_help: Usable space: {}",
442 self.term_w
443 );
444 help = wrap_help(&help.replace("{n}", "\n"), self.term_w);
445 } else {
446 sdebugln!("No");
447 }
448 write!(self.writer, "{}", help)?;
449 Ok(())
450 }
451
452 /// Writes argument's help to the wrapped stream.
453 fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> {
454 debugln!("Help::help;");
455 let h = if self.use_long && arg.name() != "" {
456 arg.long_help().unwrap_or_else(|| arg.help().unwrap_or(""))
457 } else {
458 arg.help().unwrap_or_else(|| arg.long_help().unwrap_or(""))
459 };
460 let mut help = String::from(h) + spec_vals;
461 let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || (self.use_long && arg.name() != "");
462 debugln!("Help::help: Next Line...{:?}", nlh);
463
464 let spcs = if nlh || self.force_next_line {
465 12 // "tab" * 3
466 } else {
467 self.longest + 12
468 };
469
470 let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= self.term_w;
471
472 // Is help on next line, if so then indent
473 if nlh || self.force_next_line {
474 write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?;
475 }
476
477 debug!("Help::help: Too long...");
478 if too_long && spcs <= self.term_w || h.contains("{n}") {
479 sdebugln!("Yes");
480 debugln!("Help::help: help...{}", help);
481 debugln!("Help::help: help width...{}", str_width(&*help));
482 // Determine how many newlines we need to insert
483 let avail_chars = self.term_w - spcs;
484 debugln!("Help::help: Usable space...{}", avail_chars);
485 help = wrap_help(&help.replace("{n}", "\n"), avail_chars);
486 } else {
487 sdebugln!("No");
488 }
489 if let Some(part) = help.lines().next() {
490 write!(self.writer, "{}", part)?;
491 }
492 for part in help.lines().skip(1) {
493 write!(self.writer, "\n")?;
494 if nlh || self.force_next_line {
495 write!(self.writer, "{}{}{}", TAB, TAB, TAB)?;
496 } else if arg.has_switch() {
497 write_nspaces!(self.writer, self.longest + 12);
498 } else {
499 write_nspaces!(self.writer, self.longest + 8);
500 }
501 write!(self.writer, "{}", part)?;
502 }
503 if !help.contains('\n') && (nlh || self.force_next_line) {
504 write!(self.writer, "\n")?;
505 }
506 Ok(())
507 }
508
509 fn spec_vals(&self, a: &ArgWithDisplay) -> String {
510 debugln!("Help::spec_vals: a={}", a);
511 let mut spec_vals = vec![];
512 if let Some(ref env) = a.env() {
513 debugln!(
514 "Help::spec_vals: Found environment variable...[{:?}:{:?}]",
515 env.0,
516 env.1
517 );
518 let env_val = if !a.is_set(ArgSettings::HideEnvValues) {
519 format!(
520 "={}",
521 env.1.map_or(Cow::Borrowed(""), |val| val.to_string_lossy())
522 )
523 } else {
524 String::new()
525 };
526 let env_info = format!(" [env: {}{}]", env.0.to_string_lossy(), env_val);
527 spec_vals.push(env_info);
528 }
529 if !a.is_set(ArgSettings::HideDefaultValue) {
530 if let Some(pv) = a.default_val() {
531 debugln!("Help::spec_vals: Found default value...[{:?}]", pv);
532 spec_vals.push(format!(
533 " [default: {}]",
534 if self.color {
535 self.cizer.good(pv.to_string_lossy())
536 } else {
537 Format::None(pv.to_string_lossy())
538 }
539 ));
540 }
541 }
542 if let Some(ref aliases) = a.aliases() {
543 debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
544 spec_vals.push(format!(
545 " [aliases: {}]",
546 if self.color {
547 aliases
548 .iter()
549 .map(|v| format!("{}", self.cizer.good(v)))
550 .collect::<Vec<_>>()
551 .join(", ")
552 } else {
553 aliases.join(", ")
554 }
555 ));
556 }
557 if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) {
558 if let Some(pv) = a.possible_vals() {
559 debugln!("Help::spec_vals: Found possible vals...{:?}", pv);
560 spec_vals.push(if self.color {
561 format!(
562 " [possible values: {}]",
563 pv.iter()
564 .map(|v| format!("{}", self.cizer.good(v)))
565 .collect::<Vec<_>>()
566 .join(", ")
567 )
568 } else {
569 format!(" [possible values: {}]", pv.join(", "))
570 });
571 }
572 }
573 spec_vals.join(" ")
574 }
575 }
576
577 fn should_show_arg(use_long: bool, arg: &ArgWithOrder) -> bool {
578 if arg.is_set(ArgSettings::Hidden) {
579 return false;
580 }
581
582 (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long)
583 || (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long)
584 || arg.is_set(ArgSettings::NextLineHelp)
585 }
586
587 // Methods to write Parser help.
588 impl<'a> Help<'a> {
589 /// Writes help for all arguments (options, flags, args, subcommands)
590 /// including titles of a Parser Object to the wrapped stream.
591 #[cfg_attr(feature = "lints", allow(useless_let_if_seq))]
592 #[cfg_attr(feature = "cargo-clippy", allow(useless_let_if_seq))]
593 pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
594 debugln!("Help::write_all_args;");
595 let flags = parser.has_flags();
596 let pos = parser
597 .positionals()
598 .filter(|arg| !arg.is_set(ArgSettings::Hidden))
599 .count() > 0;
600 let opts = parser.has_opts();
601 let subcmds = parser.has_visible_subcommands();
602
603 let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
604
605 let mut first = true;
606
607 if unified_help && (flags || opts) {
608 let opts_flags = parser
609 .flags()
610 .map(as_arg_trait)
611 .chain(parser.opts().map(as_arg_trait));
612 color!(self, "OPTIONS:\n", warning)?;
613 self.write_args(opts_flags)?;
614 first = false;
615 } else {
616 if flags {
617 color!(self, "FLAGS:\n", warning)?;
618 self.write_args(parser.flags().map(as_arg_trait))?;
619 first = false;
620 }
621 if opts {
622 if !first {
623 self.writer.write_all(b"\n\n")?;
624 }
625 color!(self, "OPTIONS:\n", warning)?;
626 self.write_args(parser.opts().map(as_arg_trait))?;
627 first = false;
628 }
629 }
630
631 if pos {
632 if !first {
633 self.writer.write_all(b"\n\n")?;
634 }
635 color!(self, "ARGS:\n", warning)?;
636 self.write_args_unsorted(parser.positionals().map(as_arg_trait))?;
637 first = false;
638 }
639
640 if subcmds {
641 if !first {
642 self.writer.write_all(b"\n\n")?;
643 }
644 color!(self, "SUBCOMMANDS:\n", warning)?;
645 self.write_subcommands(parser)?;
646 }
647
648 Ok(())
649 }
650
651 /// Writes help for subcommands of a Parser Object to the wrapped stream.
652 fn write_subcommands(&mut self, parser: &Parser) -> io::Result<()> {
653 debugln!("Help::write_subcommands;");
654 // The shortest an arg can legally be is 2 (i.e. '-x')
655 self.longest = 2;
656 let mut ord_m = VecMap::new();
657 for sc in parser
658 .subcommands
659 .iter()
660 .filter(|s| !s.p.is_set(AppSettings::Hidden))
661 {
662 let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new());
663 self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str()));
664 //self.longest = cmp::max(self.longest, sc.p.meta.name.len());
665 btm.insert(sc.p.meta.name.clone(), sc.clone());
666 }
667
668 let mut first = true;
669 for btm in ord_m.values() {
670 for sc in btm.values() {
671 if first {
672 first = false;
673 } else {
674 self.writer.write_all(b"\n")?;
675 }
676 self.write_arg(sc)?;
677 }
678 }
679 Ok(())
680 }
681
682 /// Writes version of a Parser Object to the wrapped stream.
683 fn write_version(&mut self, parser: &Parser) -> io::Result<()> {
684 debugln!("Help::write_version;");
685 write!(self.writer, "{}", parser.meta.version.unwrap_or(""))?;
686 Ok(())
687 }
688
689 /// Writes binary name of a Parser Object to the wrapped stream.
690 fn write_bin_name(&mut self, parser: &Parser) -> io::Result<()> {
691 debugln!("Help::write_bin_name;");
692 macro_rules! write_name {
693 () => {{
694 let mut name = parser.meta.name.clone();
695 name = name.replace("{n}", "\n");
696 color!(self, wrap_help(&name, self.term_w), good)?;
697 }};
698 }
699 if let Some(bn) = parser.meta.bin_name.as_ref() {
700 if bn.contains(' ') {
701 // Incase we're dealing with subcommands i.e. git mv is translated to git-mv
702 color!(self, bn.replace(" ", "-"), good)?
703 } else {
704 write_name!();
705 }
706 } else {
707 write_name!();
708 }
709 Ok(())
710 }
711
712 /// Writes default help for a Parser Object to the wrapped stream.
713 pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> {
714 debugln!("Help::write_default_help;");
715 if let Some(h) = parser.meta.pre_help {
716 self.write_before_after_help(h)?;
717 self.writer.write_all(b"\n\n")?;
718 }
719
720 macro_rules! write_thing {
721 ($thing:expr) => {{
722 let mut owned_thing = $thing.to_owned();
723 owned_thing = owned_thing.replace("{n}", "\n");
724 write!(self.writer, "{}\n", wrap_help(&owned_thing, self.term_w))?
725 }};
726 }
727 // Print the version
728 self.write_bin_name(parser)?;
729 self.writer.write_all(b" ")?;
730 self.write_version(parser)?;
731 self.writer.write_all(b"\n")?;
732 if let Some(author) = parser.meta.author {
733 write_thing!(author)
734 }
735 // if self.use_long {
736 // if let Some(about) = parser.meta.long_about {
737 // debugln!("Help::write_default_help: writing long about");
738 // write_thing!(about)
739 // } else if let Some(about) = parser.meta.about {
740 // debugln!("Help::write_default_help: writing about");
741 // write_thing!(about)
742 // }
743 // } else
744 if let Some(about) = parser.meta.long_about {
745 debugln!("Help::write_default_help: writing long about");
746 write_thing!(about)
747 } else if let Some(about) = parser.meta.about {
748 debugln!("Help::write_default_help: writing about");
749 write_thing!(about)
750 }
751
752 color!(self, "\nUSAGE:", warning)?;
753 write!(
754 self.writer,
755 "\n{}{}\n\n",
756 TAB,
757 usage::create_usage_no_title(parser, &[])
758 )?;
759
760 let flags = parser.has_flags();
761 let pos = parser.has_positionals();
762 let opts = parser.has_opts();
763 let subcmds = parser.has_subcommands();
764
765 if flags || opts || pos || subcmds {
766 self.write_all_args(parser)?;
767 }
768
769 if let Some(h) = parser.meta.more_help {
770 if flags || opts || pos || subcmds {
771 self.writer.write_all(b"\n\n")?;
772 }
773 self.write_before_after_help(h)?;
774 }
775
776 self.writer.flush().map_err(Error::from)
777 }
778 }
779
780 /// Possible results for a copying function that stops when a given
781 /// byte was found.
782 enum CopyUntilResult {
783 DelimiterFound(usize),
784 DelimiterNotFound(usize),
785 ReaderEmpty,
786 ReadError(io::Error),
787 WriteError(io::Error),
788 }
789
790 /// Copies the contents of a reader into a writer until a delimiter byte is found.
791 /// On success, the total number of bytes that were
792 /// copied from reader to writer is returned.
793 fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> CopyUntilResult {
794 debugln!("copy_until;");
795
796 let mut count = 0;
797 for wb in r.bytes() {
798 match wb {
799 Ok(b) => {
800 if b == delimiter_byte {
801 return CopyUntilResult::DelimiterFound(count);
802 }
803 match w.write(&[b]) {
804 Ok(c) => count += c,
805 Err(e) => return CopyUntilResult::WriteError(e),
806 }
807 }
808 Err(e) => return CopyUntilResult::ReadError(e),
809 }
810 }
811 if count > 0 {
812 CopyUntilResult::DelimiterNotFound(count)
813 } else {
814 CopyUntilResult::ReaderEmpty
815 }
816 }
817
818 /// Copies the contents of a reader into a writer until a {tag} is found,
819 /// copying the tag content to a buffer and returning its size.
820 /// In addition to errors, there are three possible outputs:
821 /// - `None`: The reader was consumed.
822 /// - `Some(Ok(0))`: No tag was captured but the reader still contains data.
823 /// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
824 fn copy_and_capture<R: Read, W: Write>(
825 r: &mut R,
826 w: &mut W,
827 tag_buffer: &mut Cursor<Vec<u8>>,
828 ) -> Option<io::Result<usize>> {
829 use self::CopyUntilResult::*;
830 debugln!("copy_and_capture;");
831
832 // Find the opening byte.
833 match copy_until(r, w, b'{') {
834 // The end of the reader was reached without finding the opening tag.
835 // (either with or without having copied data to the writer)
836 // Return None indicating that we are done.
837 ReaderEmpty | DelimiterNotFound(_) => None,
838
839 // Something went wrong.
840 ReadError(e) | WriteError(e) => Some(Err(e)),
841
842 // The opening byte was found.
843 // (either with or without having copied data to the writer)
844 DelimiterFound(_) => {
845 // Lets reset the buffer first and find out how long it is.
846 tag_buffer.set_position(0);
847 let buffer_size = tag_buffer.get_ref().len();
848
849 // Find the closing byte,limiting the reader to the length of the buffer.
850 let mut rb = r.take(buffer_size as u64);
851 match copy_until(&mut rb, tag_buffer, b'}') {
852 // We were already at the end of the reader.
853 // Return None indicating that we are done.
854 ReaderEmpty => None,
855
856 // The closing tag was found.
857 // Return the tag_length.
858 DelimiterFound(tag_length) => Some(Ok(tag_length)),
859
860 // The end of the reader was found without finding the closing tag.
861 // Write the opening byte and captured text to the writer.
862 // Return 0 indicating that nothing was captured but the reader still contains data.
863 DelimiterNotFound(not_tag_length) => match w.write(b"{") {
864 Err(e) => Some(Err(e)),
865 _ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
866 Err(e) => Some(Err(e)),
867 _ => Some(Ok(0)),
868 },
869 },
870
871 ReadError(e) | WriteError(e) => Some(Err(e)),
872 }
873 }
874 }
875 }
876
877 // Methods to write Parser help using templates.
878 impl<'a> Help<'a> {
879 /// Write help to stream for the parser in the format defined by the template.
880 ///
881 /// Tags arg given inside curly brackets:
882 /// Valid tags are:
883 /// * `{bin}` - Binary name.
884 /// * `{version}` - Version number.
885 /// * `{author}` - Author information.
886 /// * `{usage}` - Automatically generated or given usage string.
887 /// * `{all-args}` - Help for all arguments (options, flags, positionals arguments,
888 /// and subcommands) including titles.
889 /// * `{unified}` - Unified help for options and flags.
890 /// * `{flags}` - Help for flags.
891 /// * `{options}` - Help for options.
892 /// * `{positionals}` - Help for positionals arguments.
893 /// * `{subcommands}` - Help for subcommands.
894 /// * `{after-help}` - Info to be displayed after the help message.
895 /// * `{before-help}` - Info to be displayed before the help message.
896 ///
897 /// The template system is, on purpose, very simple. Therefore the tags have to written
898 /// in the lowercase and without spacing.
899 fn write_templated_help(&mut self, parser: &Parser, template: &str) -> ClapResult<()> {
900 debugln!("Help::write_templated_help;");
901 let mut tmplr = Cursor::new(&template);
902 let mut tag_buf = Cursor::new(vec![0u8; 15]);
903
904 // The strategy is to copy the template from the reader to wrapped stream
905 // until a tag is found. Depending on its value, the appropriate content is copied
906 // to the wrapped stream.
907 // The copy from template is then resumed, repeating this sequence until reading
908 // the complete template.
909
910 loop {
911 let tag_length = match copy_and_capture(&mut tmplr, &mut self.writer, &mut tag_buf) {
912 None => return Ok(()),
913 Some(Err(e)) => return Err(Error::from(e)),
914 Some(Ok(val)) if val > 0 => val,
915 _ => continue,
916 };
917
918 debugln!("Help::write_template_help:iter: tag_buf={};", unsafe {
919 String::from_utf8_unchecked(
920 tag_buf.get_ref()[0..tag_length]
921 .iter()
922 .map(|&i| i)
923 .collect::<Vec<_>>(),
924 )
925 });
926 match &tag_buf.get_ref()[0..tag_length] {
927 b"?" => {
928 self.writer.write_all(b"Could not decode tag name")?;
929 }
930 b"bin" => {
931 self.write_bin_name(parser)?;
932 }
933 b"version" => {
934 write!(
935 self.writer,
936 "{}",
937 parser.meta.version.unwrap_or("unknown version")
938 )?;
939 }
940 b"author" => {
941 write!(
942 self.writer,
943 "{}",
944 parser.meta.author.unwrap_or("unknown author")
945 )?;
946 }
947 b"about" => {
948 write!(
949 self.writer,
950 "{}",
951 parser.meta.about.unwrap_or("unknown about")
952 )?;
953 }
954 b"long-about" => {
955 write!(
956 self.writer,
957 "{}",
958 parser.meta.long_about.unwrap_or("unknown about")
959 )?;
960 }
961 b"usage" => {
962 write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?;
963 }
964 b"all-args" => {
965 self.write_all_args(parser)?;
966 }
967 b"unified" => {
968 let opts_flags = parser
969 .flags()
970 .map(as_arg_trait)
971 .chain(parser.opts().map(as_arg_trait));
972 self.write_args(opts_flags)?;
973 }
974 b"flags" => {
975 self.write_args(parser.flags().map(as_arg_trait))?;
976 }
977 b"options" => {
978 self.write_args(parser.opts().map(as_arg_trait))?;
979 }
980 b"positionals" => {
981 self.write_args(parser.positionals().map(as_arg_trait))?;
982 }
983 b"subcommands" => {
984 self.write_subcommands(parser)?;
985 }
986 b"after-help" => {
987 write!(
988 self.writer,
989 "{}",
990 parser.meta.more_help.unwrap_or("unknown after-help")
991 )?;
992 }
993 b"before-help" => {
994 write!(
995 self.writer,
996 "{}",
997 parser.meta.pre_help.unwrap_or("unknown before-help")
998 )?;
999 }
1000 // Unknown tag, write it back.
1001 r => {
1002 self.writer.write_all(b"{")?;
1003 self.writer.write_all(r)?;
1004 self.writer.write_all(b"}")?;
1005 }
1006 }
1007 }
1008 }
1009 }
1010
1011 fn wrap_help(help: &str, avail_chars: usize) -> String {
1012 let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false);
1013 help.lines()
1014 .map(|line| wrapper.fill(line))
1015 .collect::<Vec<String>>()
1016 .join("\n")
1017 }
1018
1019 #[cfg(test)]
1020 mod test {
1021 use super::wrap_help;
1022
1023 #[test]
1024 fn wrap_help_last_word() {
1025 let help = String::from("foo bar baz");
1026 assert_eq!(wrap_help(&help, 5), "foo\nbar\nbaz");
1027 }
1028 }