clap/app/
help.rs

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