clap/app/
help.rs

1// Std
2use std::borrow::Cow;
3use std::cmp;
4use std::collections::BTreeMap;
5use std::fmt::Display;
6use std::io::{self, Cursor, Read, Write};
7use std::usize;
8
9// Internal
10use app::parser::Parser;
11use app::usage;
12use app::{App, AppSettings};
13use args::{AnyArg, ArgSettings, DispOrder};
14use errors::{Error, Result as ClapResult};
15use fmt::{Colorizer, ColorizerOption, Format};
16use map::VecMap;
17use INTERNAL_ERROR_MSG;
18
19// Third Party
20#[cfg(feature = "wrap_help")]
21use term_size;
22use textwrap;
23use unicode_width::UnicodeWidthStr;
24
25#[cfg(not(feature = "wrap_help"))]
26mod term_size {
27    pub fn dimensions() -> Option<(usize, usize)> {
28        None
29    }
30}
31
32fn str_width(s: &str) -> usize {
33    UnicodeWidthStr::width(s)
34}
35
36const TAB: &'static str = "    ";
37
38// These are just convenient traits to make the code easier to read.
39trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
40impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T
41where
42    T: AnyArg<'b, 'c> + Display,
43{
44}
45
46trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
47    fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
48}
49impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
50where
51    T: ArgWithDisplay<'b, 'c> + DispOrder,
52{
53    fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
54        self
55    }
56}
57
58fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
59    x
60}
61
62impl<'b, 'c> DispOrder for App<'b, 'c> {
63    fn disp_ord(&self) -> usize {
64        999
65    }
66}
67
68macro_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.
88pub 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
101impl<'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.
205impl<'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
577fn 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.
588impl<'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.
782enum 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.
793fn 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`.
824fn 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.
878impl<'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
1011fn 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)]
1020mod 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}