4 use std
::collections
::BTreeMap
;
6 use std
::io
::{self, Cursor, Read, Write}
;
10 use app
::parser
::Parser
;
12 use app
::{App, AppSettings}
;
13 use args
::{AnyArg, ArgSettings, DispOrder}
;
14 use errors
::{Error, Result as ClapResult}
;
15 use fmt
::{Colorizer, ColorizerOption, Format}
;
17 use INTERNAL_ERROR_MSG
;
20 #[cfg(feature = "wrap_help")]
23 use unicode_width
::UnicodeWidthStr
;
25 #[cfg(not(feature = "wrap_help"))]
27 pub fn dimensions() -> Option
<(usize, usize)> {
32 fn str_width(s
: &str) -> usize {
33 UnicodeWidthStr
::width(s
)
36 const TAB
: &'
static str = " ";
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
42 T
: AnyArg
<'b
, 'c
> + Display
,
46 trait ArgWithOrder
<'b
, 'c
>: ArgWithDisplay
<'b
, 'c
> + DispOrder
{
47 fn as_base(&self) -> &ArgWithDisplay
<'b
, 'c
>;
49 impl<'b
, 'c
, T
> ArgWithOrder
<'b
, 'c
> for T
51 T
: ArgWithDisplay
<'b
, 'c
> + DispOrder
,
53 fn as_base(&self) -> &ArgWithDisplay
<'b
, 'c
> {
58 fn as_arg_trait
<'a
, 'b
, T
: ArgWithOrder
<'a
, 'b
>>(x
: &T
) -> &ArgWithOrder
<'a
, 'b
> {
62 impl<'b
, 'c
> DispOrder
for App
<'b
, 'c
> {
63 fn disp_ord(&self) -> usize {
69 ($_self
:ident
, $s
:expr
, $c
:ident
) => {
71 write
!($_self
.writer
, "{}", $_self
.cizer
.$
c($s
))
73 write
!($_self
.writer
, "{}", $s
)
76 ($_self
:ident
, $fmt_s
:expr
, $v
:expr
, $c
:ident
) => {
78 write
!($_self
.writer
, "{}", $_self
.cizer
.$
c(format
!($fmt_s
, $v
)))
80 write
!($_self
.writer
, $fmt_s
, $v
)
85 /// `clap` Help Writer.
87 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
89 writer
: &'a
mut Write
,
96 force_next_line
: bool
,
102 /// Create a new `Help` instance.
103 #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
106 next_line_help
: bool
,
110 term_w
: Option
<usize>,
111 max_w
: Option
<usize>,
114 debugln
!("Help::new;");
117 next_line_help
: next_line_help
,
119 term_w
: match term_w
{
120 Some(width
) => if width
== 0 {
126 term_size
::dimensions().map_or(120, |(w
, _
)| w
),
128 None
| Some(0) => usize::MAX
,
136 force_next_line
: false,
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
)
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
)
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)
164 pub fn _write_parser_help(
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
{
176 when
: parser
.color(),
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
)?
;
198 self.write_default_help(parser
)?
;
204 // Methods to write AnyArg help.
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
<()>
209 I
: Iterator
<Item
= &'d ArgWithOrder
<'b
, 'c
>>,
211 debugln
!("Help::write_args_unsorted;");
212 // The shortest an arg can legally be is 2 (i.e. '-x')
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()));
222 let mut first
= true;
227 self.writer
.write_all(b
"\n")?
;
229 self.write_arg(arg
.as_base())?
;
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
<()>
237 I
: Iterator
<Item
= &'d ArgWithOrder
<'b
, 'c
>>,
239 debugln
!("Help::write_args;");
240 // The shortest an arg can legally be is 2 (i.e. '-x')
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
249 should_show_arg(use_long
, *arg
)
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
);
256 let btm
= ord_m
.entry(arg
.disp_ord()).or_insert(BTreeMap
::new());
257 btm
.insert(arg
.name(), arg
);
259 let mut first
= true;
260 for btm
in ord_m
.values() {
261 for arg
in btm
.values() {
265 self.writer
.write_all(b
"\n")?
;
267 self.write_arg(arg
.as_base())?
;
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;");
278 let spec_vals
= self.val(arg
)?
;
279 self.help(arg
, &*spec_vals
)?
;
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
)
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() {
302 if arg
.takes_value() {
303 if let Some(l
) = arg
.long() {
304 if arg
.short().is_some() {
305 write
!(self.writer
, ", ")?
;
307 color
!(self, "--{}", l
, good
)?
310 let sep
= if arg
.is_set(ArgSettings
::RequireEquals
) {
315 write
!(self.writer
, "{}", sep
)?
;
316 } else if let Some(l
) = arg
.long() {
317 if arg
.short().is_some() {
318 write
!(self.writer
, ", ")?
;
320 color
!(self, "--{}", l
, good
)?
;
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
)
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
)?
;
343 if arg
.is_set(ArgSettings
::Multiple
) && num
== 1 {
344 color
!(self, "...", good
)?
;
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
)?
;
354 if arg
.is_set(ArgSettings
::Multiple
) && num
== 1 {
355 color
!(self, "...", good
)?
;
357 } else if arg
.has_switch() {
358 color
!(self, "<{}>", arg
.name(), good
)?
;
359 if arg
.is_set(ArgSettings
::Multiple
) {
360 color
!(self, "...", good
)?
;
363 color
!(self, "{}", arg
, good
)?
;
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
);
376 debug
!("Help::val: Has switch...");
377 if arg
.has_switch() {
379 debugln
!("Help::val: force_next_line...{:?}", self.force_next_line
);
380 debugln
!("Help::val: nlh...{:?}", nlh
);
381 debugln
!("Help::val: taken...{}", taken
);
383 "Help::val: help_width > (width - taken)...{} > ({} - {})",
388 debugln
!("Help::val: longest...{}", self.longest
);
389 debug
!("Help::val: next_line...");
390 if !(nlh
|| self.force_next_line
) {
392 let self_len
= str_width(arg
.to_string().as_str());
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
401 // Only account for ', --' + 4 after the val
405 write_nspaces
!(self.writer
, spcs
);
409 } else if !(nlh
|| self.force_next_line
) {
410 sdebugln
!("No, and not next_line");
413 self.longest
+ 4 - (str_width(arg
.to_string().as_str()))
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
426 "Help::write_before_after_help: Term width...{}",
429 let too_long
= str_width(h
) >= self.term_w
;
431 debug
!("Help::write_before_after_help: Too long...");
432 if too_long
|| h
.contains("{n}") {
434 debugln
!("Help::write_before_after_help: help: {}", help
);
436 "Help::write_before_after_help: help width: {}",
439 // Determine how many newlines we need to insert
441 "Help::write_before_after_help: Usable space: {}",
444 help
= wrap_help(&help
.replace("{n}", "\n"), self.term_w
);
448 write
!(self.writer
, "{}", help
)?
;
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(""))
458 arg
.help().unwrap_or_else(|| arg
.long_help().unwrap_or(""))
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
);
464 let spcs
= if nlh
|| self.force_next_line
{
470 let too_long
= spcs
+ str_width(h
) + str_width(&*spec_vals
) >= self.term_w
;
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
)?
;
477 debug
!("Help::help: Too long...");
478 if too_long
&& spcs
<= self.term_w
|| h
.contains("{n}") {
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
);
489 if let Some(part
) = help
.lines().next() {
490 write
!(self.writer
, "{}", part
)?
;
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);
499 write_nspaces
!(self.writer
, self.longest
+ 8);
501 write
!(self.writer
, "{}", part
)?
;
503 if !help
.contains('
\n'
) && (nlh
|| self.force_next_line
) {
504 write
!(self.writer
, "\n")?
;
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() {
514 "Help::spec_vals: Found environment variable...[{:?}:{:?}]",
518 let env_val
= if !a
.is_set(ArgSettings
::HideEnvValues
) {
521 env
.1.map_or(Cow
::Borrowed(""), |val
| val
.to_string_lossy())
526 let env_info
= format
!(" [env: {}{}]", env
.0.to_string_lossy(), env_val
);
527 spec_vals
.push(env_info
);
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
!(
535 self.cizer
.good(pv
.to_string_lossy())
537 Format
::None(pv
.to_string_lossy())
542 if let Some(ref aliases
) = a
.aliases() {
543 debugln
!("Help::spec_vals: Found aliases...{:?}", aliases
);
544 spec_vals
.push(format
!(
549 .map(|v
| format
!("{}", self.cizer
.good(v
)))
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
{
562 " [possible values: {}]",
564 .map(|v
| format
!("{}", self.cizer
.good(v
)))
569 format
!(" [possible values: {}]", pv
.join(", "))
577 fn should_show_arg(use_long
: bool
, arg
: &ArgWithOrder
) -> bool
{
578 if arg
.is_set(ArgSettings
::Hidden
) {
582 (!arg
.is_set(ArgSettings
::HiddenLongHelp
) && use_long
)
583 || (!arg
.is_set(ArgSettings
::HiddenShortHelp
) && !use_long
)
584 || arg
.is_set(ArgSettings
::NextLineHelp
)
587 // Methods to write Parser help.
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();
598 .filter(|arg
| !arg
.is_set(ArgSettings
::Hidden
))
600 let opts
= parser
.has_opts();
601 let subcmds
= parser
.has_visible_subcommands();
603 let unified_help
= parser
.is_set(AppSettings
::UnifiedHelpMessage
);
605 let mut first
= true;
607 if unified_help
&& (flags
|| opts
) {
608 let opts_flags
= parser
611 .chain(parser
.opts().map(as_arg_trait
));
612 color
!(self, "OPTIONS:\n", warning
)?
;
613 self.write_args(opts_flags
)?
;
617 color
!(self, "FLAGS:\n", warning
)?
;
618 self.write_args(parser
.flags().map(as_arg_trait
))?
;
623 self.writer
.write_all(b
"\n\n")?
;
625 color
!(self, "OPTIONS:\n", warning
)?
;
626 self.write_args(parser
.opts().map(as_arg_trait
))?
;
633 self.writer
.write_all(b
"\n\n")?
;
635 color
!(self, "ARGS:\n", warning
)?
;
636 self.write_args_unsorted(parser
.positionals().map(as_arg_trait
))?
;
642 self.writer
.write_all(b
"\n\n")?
;
644 color
!(self, "SUBCOMMANDS:\n", warning
)?
;
645 self.write_subcommands(parser
)?
;
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')
656 let mut ord_m
= VecMap
::new();
660 .filter(|s
| !s
.p
.is_set(AppSettings
::Hidden
))
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());
668 let mut first
= true;
669 for btm
in ord_m
.values() {
670 for sc
in btm
.values() {
674 self.writer
.write_all(b
"\n")?
;
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(""))?
;
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
{
694 let mut name
= parser
.meta
.name
.clone();
695 name
= name
.replace("{n}", "\n");
696 color
!(self, wrap_help(&name
, self.term_w
), good
)?
;
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
)?
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")?
;
720 macro_rules
! write_thing
{
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
))?
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
{
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)
744 if let Some(about
) = parser
.meta
.long_about
{
745 debugln
!("Help::write_default_help: writing long about");
747 } else if let Some(about
) = parser
.meta
.about
{
748 debugln
!("Help::write_default_help: writing about");
752 color
!(self, "\nUSAGE:", warning
)?
;
757 usage
::create_usage_no_title(parser
, &[])
760 let flags
= parser
.has_flags();
761 let pos
= parser
.has_positionals();
762 let opts
= parser
.has_opts();
763 let subcmds
= parser
.has_subcommands();
765 if flags
|| opts
|| pos
|| subcmds
{
766 self.write_all_args(parser
)?
;
769 if let Some(h
) = parser
.meta
.more_help
{
770 if flags
|| opts
|| pos
|| subcmds
{
771 self.writer
.write_all(b
"\n\n")?
;
773 self.write_before_after_help(h
)?
;
776 self.writer
.flush().map_err(Error
::from
)
780 /// Possible results for a copying function that stops when a given
782 enum CopyUntilResult
{
783 DelimiterFound(usize),
784 DelimiterNotFound(usize),
786 ReadError(io
::Error
),
787 WriteError(io
::Error
),
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;");
797 for wb
in r
.bytes() {
800 if b
== delimiter_byte
{
801 return CopyUntilResult
::DelimiterFound(count
);
803 match w
.write(&[b
]) {
805 Err(e
) => return CopyUntilResult
::WriteError(e
),
808 Err(e
) => return CopyUntilResult
::ReadError(e
),
812 CopyUntilResult
::DelimiterNotFound(count
)
814 CopyUntilResult
::ReaderEmpty
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
>(
827 tag_buffer
: &mut Cursor
<Vec
<u8>>,
828 ) -> Option
<io
::Result
<usize>> {
829 use self::CopyUntilResult
::*;
830 debugln
!("copy_and_capture;");
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
,
839 // Something went wrong.
840 ReadError(e
) | WriteError(e
) => Some(Err(e
)),
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();
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.
856 // The closing tag was found.
857 // Return the tag_length.
858 DelimiterFound(tag_length
) => Some(Ok(tag_length
)),
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
)),
871 ReadError(e
) | WriteError(e
) => Some(Err(e
)),
877 // Methods to write Parser help using templates.
879 /// Write help to stream for the parser in the format defined by the template.
881 /// Tags arg given inside curly brackets:
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.
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]);
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.
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
,
918 debugln
!("Help::write_template_help:iter: tag_buf={};", unsafe {
919 String
::from_utf8_unchecked(
920 tag_buf
.get_ref()[0..tag_length
]
923 .collect
::<Vec
<_
>>(),
926 match &tag_buf
.get_ref()[0..tag_length
] {
928 self.writer
.write_all(b
"Could not decode tag name")?
;
931 self.write_bin_name(parser
)?
;
937 parser
.meta
.version
.unwrap_or("unknown version")
944 parser
.meta
.author
.unwrap_or("unknown author")
951 parser
.meta
.about
.unwrap_or("unknown about")
958 parser
.meta
.long_about
.unwrap_or("unknown about")
962 write
!(self.writer
, "{}", usage
::create_usage_no_title(parser
, &[]))?
;
965 self.write_all_args(parser
)?
;
968 let opts_flags
= parser
971 .chain(parser
.opts().map(as_arg_trait
));
972 self.write_args(opts_flags
)?
;
975 self.write_args(parser
.flags().map(as_arg_trait
))?
;
978 self.write_args(parser
.opts().map(as_arg_trait
))?
;
981 self.write_args(parser
.positionals().map(as_arg_trait
))?
;
984 self.write_subcommands(parser
)?
;
990 parser
.meta
.more_help
.unwrap_or("unknown after-help")
997 parser
.meta
.pre_help
.unwrap_or("unknown before-help")
1000 // Unknown tag, write it back.
1002 self.writer
.write_all(b
"{")?
;
1003 self.writer
.write_all(r
)?
;
1004 self.writer
.write_all(b
"}")?
;
1011 fn wrap_help(help
: &str, avail_chars
: usize) -> String
{
1012 let wrapper
= textwrap
::Wrapper
::new(avail_chars
).break_words(false);
1014 .map(|line
| wrapper
.fill(line
))
1015 .collect
::<Vec
<String
>>()
1021 use super::wrap_help
;
1024 fn wrap_help_last_word() {
1025 let help
= String
::from("foo bar baz");
1026 assert_eq
!(wrap_help(&help
, 5), "foo\nbar\nbaz");