3 use std
::collections
::BTreeMap
;
5 use std
::io
::{self, Cursor, Read, Write}
;
9 use app
::{App, AppSettings}
;
10 use app
::parser
::Parser
;
11 use args
::{AnyArg, ArgSettings, DispOrder}
;
12 use errors
::{Error, Result as ClapResult}
;
13 use fmt
::{Format, Colorizer, ColorizerOption}
;
17 use unicode_width
::UnicodeWidthStr
;
18 #[cfg(feature = "wrap_help")]
23 #[cfg(not(feature = "wrap_help"))]
25 pub fn dimensions() -> Option
<(usize, usize)> { None }
28 fn str_width(s
: &str) -> usize { UnicodeWidthStr::width(s) }
30 const TAB
: &'
static str = " ";
32 // These are just convenient traits to make the code easier to read.
33 trait ArgWithDisplay
<'b
, 'c
>: AnyArg
<'b
, 'c
> + Display {}
34 impl<'b
, 'c
, T
> ArgWithDisplay
<'b
, 'c
> for T
where T
: AnyArg
<'b
, 'c
> + Display {}
36 trait ArgWithOrder
<'b
, 'c
>: ArgWithDisplay
<'b
, 'c
> + DispOrder
{
37 fn as_base(&self) -> &ArgWithDisplay
<'b
, 'c
>;
39 impl<'b
, 'c
, T
> ArgWithOrder
<'b
, 'c
> for T
40 where T
: ArgWithDisplay
<'b
, 'c
> + DispOrder
42 fn as_base(&self) -> &ArgWithDisplay
<'b
, 'c
> { self }
45 fn as_arg_trait
<'a
, 'b
, T
: ArgWithOrder
<'a
, 'b
>>(x
: &T
) -> &ArgWithOrder
<'a
, 'b
> { x }
47 impl<'b
, 'c
> DispOrder
for App
<'b
, 'c
> {
48 fn disp_ord(&self) -> usize { 999 }
52 ($_self
:ident
, $s
:expr
, $c
:ident
) => {
54 write
!($_self
.writer
, "{}", $_self
.cizer
.$
c($s
))
56 write
!($_self
.writer
, "{}", $s
)
59 ($_self
:ident
, $fmt_s
:expr
, $v
:expr
, $c
:ident
) => {
61 write
!($_self
.writer
, "{}", $_self
.cizer
.$
c(format
!($fmt_s
, $v
)))
63 write
!($_self
.writer
, $fmt_s
, $v
)
68 /// `clap` Help Writer.
70 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
72 writer
: &'a
mut Write
,
79 force_next_line
: bool
,
85 /// Create a new `Help` instance.
86 #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
87 pub fn new(w
: &'a
mut Write
,
92 term_w
: Option
<usize>,
96 debugln
!("Help::new;");
99 next_line_help
: next_line_help
,
101 term_w
: match term_w
{
102 Some(width
) => if width
== 0 { usize::MAX }
else { width }
,
104 cmp
::min(term_size
::dimensions().map_or(120, |(w
, _
)| w
),
106 None
| Some(0) => usize::MAX
,
114 force_next_line
: false,
119 /// Reads help settings from an App
120 /// and write its help to the wrapped stream.
121 pub fn write_app_help(w
: &'a
mut Write
, app
: &App
, use_long
: bool
) -> ClapResult
<()> {
122 debugln
!("Help::write_app_help;");
123 Self::write_parser_help(w
, &app
.p
, use_long
)
126 /// Reads help settings from a Parser
127 /// and write its help to the wrapped stream.
128 pub fn write_parser_help(w
: &'a
mut Write
, parser
: &Parser
, use_long
: bool
) -> ClapResult
<()> {
129 debugln
!("Help::write_parser_help;");
130 Self::_write_parser_help(w
, parser
, false, use_long
)
133 /// Reads help settings from a Parser
134 /// and write its help to the wrapped stream which will be stderr. This method prevents
135 /// formatting when required.
136 pub fn write_parser_help_to_stderr(w
: &'a
mut Write
, parser
: &Parser
) -> ClapResult
<()> {
137 debugln
!("Help::write_parser_help;");
138 Self::_write_parser_help(w
, parser
, true, false)
142 pub fn _write_parser_help(w
: &'a
mut Write
, parser
: &Parser
, stderr
: bool
, use_long
: bool
) -> ClapResult
<()> {
143 debugln
!("Help::write_parser_help;");
144 let nlh
= parser
.is_set(AppSettings
::NextLineHelp
);
145 let hide_v
= parser
.is_set(AppSettings
::HidePossibleValuesInHelp
);
146 let color
= parser
.is_set(AppSettings
::ColoredHelp
);
147 let cizer
= Colorizer
::new(ColorizerOption
{
149 when
: parser
.color(),
162 /// Writes the parser help to the wrapped stream.
163 pub fn write_help(&mut self, parser
: &Parser
) -> ClapResult
<()> {
164 debugln
!("Help::write_help;");
165 if let Some(h
) = parser
.meta
.help_str
{
166 write
!(self.writer
, "{}", h
).map_err(Error
::from
)?
;
167 } else if let Some(tmpl
) = parser
.meta
.template
{
168 self.write_templated_help(parser
, tmpl
)?
;
170 self.write_default_help(parser
)?
;
176 // Methods to write AnyArg help.
178 /// Writes help for each argument in the order they were declared to the wrapped stream.
179 fn write_args_unsorted
<'b
: 'd
, 'c
: 'd
, 'd
, I
: 'd
>(&mut self, args
: I
) -> io
::Result
<()>
180 where I
: Iterator
<Item
= &'d ArgWithOrder
<'b
, 'c
>>
182 debugln
!("Help::write_args_unsorted;");
183 // The shortest an arg can legally be is 2 (i.e. '-x')
185 let mut arg_v
= Vec
::with_capacity(10);
186 for arg
in args
.filter(|arg
| {
187 !(arg
.is_set(ArgSettings
::Hidden
)) ||
188 arg
.is_set(ArgSettings
::NextLineHelp
)
190 if arg
.longest_filter() {
191 self.longest
= cmp
::max(self.longest
, str_width(arg
.to_string().as_str()));
195 let mut first
= true;
200 self.writer
.write_all(b
"\n")?
;
202 self.write_arg(arg
.as_base())?
;
207 /// Sorts arguments by length and display order and write their help to the wrapped stream.
208 fn write_args
<'b
: 'd
, 'c
: 'd
, 'd
, I
: 'd
>(&mut self, args
: I
) -> io
::Result
<()>
209 where I
: Iterator
<Item
= &'d ArgWithOrder
<'b
, 'c
>>
211 debugln
!("Help::write_args;");
212 // The shortest an arg can legally be is 2 (i.e. '-x')
214 let mut ord_m
= VecMap
::new();
215 // Determine the longest
216 for arg
in args
.filter(|arg
| {
217 // If it's NextLineHelp, but we don't care to compute how long because it may be
218 // NextLineHelp on purpose *because* it's so long and would throw off all other
220 !arg
.is_set(ArgSettings
::Hidden
) || arg
.is_set(ArgSettings
::NextLineHelp
)
222 if arg
.longest_filter() {
223 debugln
!("Help::write_args: Current Longest...{}", self.longest
);
224 self.longest
= cmp
::max(self.longest
, str_width(arg
.to_string().as_str()));
225 debugln
!("Help::write_args: New Longest...{}", self.longest
);
227 let btm
= ord_m
.entry(arg
.disp_ord()).or_insert(BTreeMap
::new());
228 btm
.insert(arg
.name(), arg
);
230 let mut first
= true;
231 for btm
in ord_m
.values() {
232 for arg
in btm
.values() {
236 self.writer
.write_all(b
"\n")?
;
238 self.write_arg(arg
.as_base())?
;
244 /// Writes help for an argument to the wrapped stream.
245 fn write_arg
<'b
, 'c
>(&mut self, arg
: &ArgWithDisplay
<'b
, 'c
>) -> io
::Result
<()> {
246 debugln
!("Help::write_arg;");
249 let spec_vals
= self.val(arg
)?
;
250 self.help(arg
, &*spec_vals
)?
;
254 /// Writes argument's short command to the wrapped stream.
255 fn short
<'b
, 'c
>(&mut self, arg
: &ArgWithDisplay
<'b
, 'c
>) -> io
::Result
<()> {
256 debugln
!("Help::short;");
257 write
!(self.writer
, "{}", TAB
)?
;
258 if let Some(s
) = arg
.short() {
259 color
!(self, "-{}", s
, good
)
260 } else if arg
.has_switch() {
261 write
!(self.writer
, "{}", TAB
)
267 /// Writes argument's long command to the wrapped stream.
268 fn long
<'b
, 'c
>(&mut self, arg
: &ArgWithDisplay
<'b
, 'c
>) -> io
::Result
<()> {
269 debugln
!("Help::long;");
270 if !arg
.has_switch() {
273 if arg
.takes_value() {
274 if let Some(l
) = arg
.long() {
275 if arg
.short().is_some() {
276 write
!(self.writer
, ", ")?
;
278 color
!(self, "--{}", l
, good
)?
281 let sep
= if arg
.is_set(ArgSettings
::RequireEquals
) {
286 write
!(self.writer
, "{}", sep
)?
;
287 } else if let Some(l
) = arg
.long() {
288 if arg
.short().is_some() {
289 write
!(self.writer
, ", ")?
;
291 color
!(self, "--{}", l
, good
)?
;
296 /// Writes argument's possible values to the wrapped stream.
297 fn val
<'b
, 'c
>(&mut self, arg
: &ArgWithDisplay
<'b
, 'c
>) -> Result
<String
, io
::Error
> {
298 debugln
!("Help::val: arg={}", arg
);
299 if arg
.takes_value() {
300 if let Some(vec
) = arg
.val_names() {
301 let mut it
= vec
.iter().peekable();
302 while let Some((_
, val
)) = it
.next() {
303 color
!(self, "<{}>", val
, good
)?
;
304 if it
.peek().is_some() {
305 write
!(self.writer
, " ")?
;
309 if arg
.is_set(ArgSettings
::Multiple
) && num
== 1 {
310 color
!(self, "...", good
)?
;
312 } else if let Some(num
) = arg
.num_vals() {
313 let mut it
= (0..num
).peekable();
314 while let Some(_
) = it
.next() {
315 color
!(self, "<{}>", arg
.name(), good
)?
;
316 if it
.peek().is_some() {
317 write
!(self.writer
, " ")?
;
320 if arg
.is_set(ArgSettings
::Multiple
) && num
== 1 {
321 color
!(self, "...", good
)?
;
323 } else if arg
.has_switch() {
324 color
!(self, "<{}>", arg
.name(), good
)?
;
325 if arg
.is_set(ArgSettings
::Multiple
) {
326 color
!(self, "...", good
)?
;
329 color
!(self, "{}", arg
, good
)?
;
333 let spec_vals
= self.spec_vals(arg
);
334 let h
= arg
.help().unwrap_or("");
335 let h_w
= str_width(h
) + str_width(&*spec_vals
);
336 let nlh
= self.next_line_help
|| arg
.is_set(ArgSettings
::NextLineHelp
);
337 let taken
= self.longest
+ 12;
338 self.force_next_line
= !nlh
&& self.term_w
>= taken
&&
339 (taken
as f32 / self.term_w
as f32) > 0.40 &&
340 h_w
> (self.term_w
- taken
);
342 debug
!("Help::val: Has switch...");
343 if arg
.has_switch() {
345 debugln
!("Help::val: force_next_line...{:?}", self.force_next_line
);
346 debugln
!("Help::val: nlh...{:?}", nlh
);
347 debugln
!("Help::val: taken...{}", taken
);
348 debugln
!("Help::val: help_width > (width - taken)...{} > ({} - {})",
352 debugln
!("Help::val: longest...{}", self.longest
);
353 debug
!("Help::val: next_line...");
354 if !(nlh
|| self.force_next_line
) {
356 let self_len
= str_width(arg
.to_string().as_str());
358 let mut spcs
= self.longest
- self_len
;
359 // Since we're writing spaces from the tab point we first need to know if we
360 // had a long and short, or just short
361 if arg
.long().is_some() {
362 // Only account 4 after the val
365 // Only account for ', --' + 4 after the val
369 write_nspaces
!(self.writer
, spcs
);
373 } else if !(nlh
|| self.force_next_line
) {
374 sdebugln
!("No, and not next_line");
375 write_nspaces
!(self.writer
, self.longest
+ 4 - (str_width(arg
.to_string().as_str())));
382 fn write_before_after_help(&mut self, h
: &str) -> io
::Result
<()> {
383 debugln
!("Help::write_before_after_help;");
384 let mut help
= String
::from(h
);
385 // determine if our help fits or needs to wrap
386 debugln
!("Help::write_before_after_help: Term width...{}",
388 let too_long
= str_width(h
) >= self.term_w
;
390 debug
!("Help::write_before_after_help: Too long...");
391 if too_long
|| h
.contains("{n}") {
393 debugln
!("Help::write_before_after_help: help: {}", help
);
394 debugln
!("Help::write_before_after_help: help width: {}",
396 // Determine how many newlines we need to insert
397 debugln
!("Help::write_before_after_help: Usable space: {}",
399 help
= wrap_help(&help
.replace("{n}", "\n"), self.term_w
);
403 write
!(self.writer
, "{}", help
)?
;
407 /// Writes argument's help to the wrapped stream.
408 fn help
<'b
, 'c
>(&mut self, arg
: &ArgWithDisplay
<'b
, 'c
>, spec_vals
: &str) -> io
::Result
<()> {
409 debugln
!("Help::help;");
410 let h
= if self.use_long
{
411 arg
.long_help().unwrap_or_else(|| arg
.help().unwrap_or(""))
413 arg
.help().unwrap_or_else(|| arg
.long_help().unwrap_or(""))
415 let mut help
= String
::from(h
) + spec_vals
;
416 let nlh
= self.next_line_help
|| arg
.is_set(ArgSettings
::NextLineHelp
) || self.use_long
;
417 debugln
!("Help::help: Next Line...{:?}", nlh
);
419 let spcs
= if nlh
|| self.force_next_line
{
425 let too_long
= spcs
+ str_width(h
) + str_width(&*spec_vals
) >= self.term_w
;
427 // Is help on next line, if so then indent
428 if nlh
|| self.force_next_line
{
429 write
!(self.writer
, "\n{}{}{}", TAB
, TAB
, TAB
)?
;
432 debug
!("Help::help: Too long...");
433 if too_long
&& spcs
<= self.term_w
|| h
.contains("{n}") {
435 debugln
!("Help::help: help...{}", help
);
436 debugln
!("Help::help: help width...{}", str_width(&*help
));
437 // Determine how many newlines we need to insert
438 let avail_chars
= self.term_w
- spcs
;
439 debugln
!("Help::help: Usable space...{}", avail_chars
);
440 help
= wrap_help(&help
.replace("{n}", "\n"), avail_chars
);
444 if let Some(part
) = help
.lines().next() {
445 write
!(self.writer
, "{}", part
)?
;
447 for part
in help
.lines().skip(1) {
448 write
!(self.writer
, "\n")?
;
449 if nlh
|| self.force_next_line
{
450 write
!(self.writer
, "{}{}{}", TAB
, TAB
, TAB
)?
;
451 } else if arg
.has_switch() {
452 write_nspaces
!(self.writer
, self.longest
+ 12);
454 write_nspaces
!(self.writer
, self.longest
+ 8);
456 write
!(self.writer
, "{}", part
)?
;
458 if !help
.contains('
\n'
) && (nlh
|| self.force_next_line
) {
459 write
!(self.writer
, "\n")?
;
464 fn spec_vals(&self, a
: &ArgWithDisplay
) -> String
{
465 debugln
!("Help::spec_vals: a={}", a
);
466 let mut spec_vals
= vec
![];
467 if !a
.is_set(ArgSettings
::HideDefaultValue
) {
468 if let Some(pv
) = a
.default_val() {
469 debugln
!("Help::spec_vals: Found default value...[{:?}]", pv
);
470 spec_vals
.push(format
!(" [default: {}]",
472 self.cizer
.good(pv
.to_string_lossy())
474 Format
::None(pv
.to_string_lossy())
478 if let Some(ref aliases
) = a
.aliases() {
479 debugln
!("Help::spec_vals: Found aliases...{:?}", aliases
);
480 spec_vals
.push(format
!(" [aliases: {}]",
484 .map(|v
| format
!("{}", self.cizer
.good(v
)))
491 if !self.hide_pv
&& !a
.is_set(ArgSettings
::HidePossibleValues
) {
492 if let Some(pv
) = a
.possible_vals() {
493 debugln
!("Help::spec_vals: Found possible vals...{:?}", pv
);
494 spec_vals
.push(if self.color
{
495 format
!(" [values: {}]",
497 .map(|v
| format
!("{}", self.cizer
.good(v
)))
501 format
!(" [values: {}]", pv
.join(", "))
510 // Methods to write Parser help.
512 /// Writes help for all arguments (options, flags, args, subcommands)
513 /// including titles of a Parser Object to the wrapped stream.
514 #[cfg_attr(feature = "lints", allow(useless_let_if_seq))]
515 #[cfg_attr(feature = "cargo-clippy", allow(useless_let_if_seq))]
516 pub fn write_all_args(&mut self, parser
: &Parser
) -> ClapResult
<()> {
517 debugln
!("Help::write_all_args;");
518 let flags
= parser
.has_flags();
521 .filter(|arg
| !arg
.is_set(ArgSettings
::Hidden
))
523 let opts
= parser
.has_opts();
524 let subcmds
= parser
.has_visible_subcommands();
526 let unified_help
= parser
.is_set(AppSettings
::UnifiedHelpMessage
);
528 let mut first
= true;
530 if unified_help
&& (flags
|| opts
) {
531 let opts_flags
= parser
534 .chain(parser
.opts().map(as_arg_trait
));
535 color
!(self, "OPTIONS:\n", warning
)?
;
536 self.write_args(opts_flags
)?
;
540 color
!(self, "FLAGS:\n", warning
)?
;
541 self.write_args(parser
.flags().map(as_arg_trait
))?
;
546 self.writer
.write_all(b
"\n\n")?
;
548 color
!(self, "OPTIONS:\n", warning
)?
;
549 self.write_args(parser
.opts().map(as_arg_trait
))?
;
556 self.writer
.write_all(b
"\n\n")?
;
558 color
!(self, "ARGS:\n", warning
)?
;
559 self.write_args_unsorted(parser
.positionals().map(as_arg_trait
))?
;
565 self.writer
.write_all(b
"\n\n")?
;
567 color
!(self, "SUBCOMMANDS:\n", warning
)?
;
568 self.write_subcommands(parser
)?
;
574 /// Writes help for subcommands of a Parser Object to the wrapped stream.
575 fn write_subcommands(&mut self, parser
: &Parser
) -> io
::Result
<()> {
576 debugln
!("Help::write_subcommands;");
577 // The shortest an arg can legally be is 2 (i.e. '-x')
579 let mut ord_m
= VecMap
::new();
583 .filter(|s
| !s
.p
.is_set(AppSettings
::Hidden
)) {
585 .entry(sc
.p
.meta
.disp_ord
)
586 .or_insert(BTreeMap
::new());
587 self.longest
= cmp
::max(self.longest
, str_width(sc
.p
.meta
.name
.as_str()));
588 //self.longest = cmp::max(self.longest, sc.p.meta.name.len());
589 btm
.insert(sc
.p
.meta
.name
.clone(), sc
.clone());
592 let mut first
= true;
593 for btm
in ord_m
.values() {
594 for sc
in btm
.values() {
598 self.writer
.write_all(b
"\n")?
;
606 /// Writes version of a Parser Object to the wrapped stream.
607 fn write_version(&mut self, parser
: &Parser
) -> io
::Result
<()> {
608 debugln
!("Help::write_version;");
609 write
!(self.writer
, "{}", parser
.meta
.version
.unwrap_or(""))?
;
613 /// Writes binary name of a Parser Object to the wrapped stream.
614 fn write_bin_name(&mut self, parser
: &Parser
) -> io
::Result
<()> {
615 debugln
!("Help::write_bin_name;");
616 macro_rules
! write_name
{
618 let mut name
= parser
.meta
.name
.clone();
619 name
= name
.replace("{n}", "\n");
620 color
!(self, wrap_help(&name
, self.term_w
), good
)?
;
623 if let Some(bn
) = parser
.meta
.bin_name
.as_ref() {
624 if bn
.contains(' '
) {
625 // Incase we're dealing with subcommands i.e. git mv is translated to git-mv
626 color
!(self, bn
.replace(" ", "-"), good
)?
636 /// Writes default help for a Parser Object to the wrapped stream.
637 pub fn write_default_help(&mut self, parser
: &Parser
) -> ClapResult
<()> {
638 debugln
!("Help::write_default_help;");
639 if let Some(h
) = parser
.meta
.pre_help
{
640 self.write_before_after_help(h
)?
;
641 self.writer
.write_all(b
"\n\n")?
;
644 macro_rules
! write_thing
{
646 let mut owned_thing
= $thing
.to_owned();
647 owned_thing
= owned_thing
.replace("{n}", "\n");
648 write
!(self.writer
, "{}\n",
649 wrap_help(&owned_thing
, self.term_w
))?
653 self.write_bin_name(parser
)?
;
654 self.writer
.write_all(b
" ")?
;
655 self.write_version(parser
)?
;
656 self.writer
.write_all(b
"\n")?
;
657 if let Some(author
) = parser
.meta
.author
{
661 if let Some(about
) = parser
.meta
.long_about
{
662 debugln
!("Help::write_default_help: writing long about");
664 } else if let Some(about
) = parser
.meta
.about
{
665 debugln
!("Help::write_default_help: writing about");
669 if let Some(about
) = parser
.meta
.about
{
670 debugln
!("Help::write_default_help: writing about");
672 } else if let Some(about
) = parser
.meta
.long_about
{
673 debugln
!("Help::write_default_help: writing long about");
678 color
!(self, "\nUSAGE:", warning
)?
;
682 usage
::create_usage_no_title(parser
, &[]))?
;
684 let flags
= parser
.has_flags();
685 let pos
= parser
.has_positionals();
686 let opts
= parser
.has_opts();
687 let subcmds
= parser
.has_subcommands();
689 if flags
|| opts
|| pos
|| subcmds
{
690 self.write_all_args(parser
)?
;
693 if let Some(h
) = parser
.meta
.more_help
{
694 if flags
|| opts
|| pos
|| subcmds
{
695 self.writer
.write_all(b
"\n\n")?
;
697 self.write_before_after_help(h
)?
;
700 self.writer
.flush().map_err(Error
::from
)
704 /// Possible results for a copying function that stops when a given
706 enum CopyUntilResult
{
707 DelimiterFound(usize),
708 DelimiterNotFound(usize),
710 ReadError(io
::Error
),
711 WriteError(io
::Error
),
714 /// Copies the contents of a reader into a writer until a delimiter byte is found.
715 /// On success, the total number of bytes that were
716 /// copied from reader to writer is returned.
717 fn copy_until
<R
: Read
, W
: Write
>(r
: &mut R
, w
: &mut W
, delimiter_byte
: u8) -> CopyUntilResult
{
718 debugln
!("copy_until;");
721 for wb
in r
.bytes() {
724 if b
== delimiter_byte
{
725 return CopyUntilResult
::DelimiterFound(count
);
727 match w
.write(&[b
]) {
729 Err(e
) => return CopyUntilResult
::WriteError(e
),
732 Err(e
) => return CopyUntilResult
::ReadError(e
),
736 CopyUntilResult
::DelimiterNotFound(count
)
738 CopyUntilResult
::ReaderEmpty
742 /// Copies the contents of a reader into a writer until a {tag} is found,
743 /// copying the tag content to a buffer and returning its size.
744 /// In addition to errors, there are three possible outputs:
745 /// - `None`: The reader was consumed.
746 /// - `Some(Ok(0))`: No tag was captured but the reader still contains data.
747 /// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
748 fn copy_and_capture
<R
: Read
, W
: Write
>(r
: &mut R
,
750 tag_buffer
: &mut Cursor
<Vec
<u8>>)
751 -> Option
<io
::Result
<usize>> {
752 use self::CopyUntilResult
::*;
753 debugln
!("copy_and_capture;");
755 // Find the opening byte.
756 match copy_until(r
, w
, b'
{'
) {
758 // The end of the reader was reached without finding the opening tag.
759 // (either with or without having copied data to the writer)
760 // Return None indicating that we are done.
762 DelimiterNotFound(_
) => None
,
764 // Something went wrong.
765 ReadError(e
) | WriteError(e
) => Some(Err(e
)),
767 // The opening byte was found.
768 // (either with or without having copied data to the writer)
769 DelimiterFound(_
) => {
771 // Lets reset the buffer first and find out how long it is.
772 tag_buffer
.set_position(0);
773 let buffer_size
= tag_buffer
.get_ref().len();
775 // Find the closing byte,limiting the reader to the length of the buffer.
776 let mut rb
= r
.take(buffer_size
as u64);
777 match copy_until(&mut rb
, tag_buffer
, b'
}'
) {
779 // We were already at the end of the reader.
780 // Return None indicating that we are done.
783 // The closing tag was found.
784 // Return the tag_length.
785 DelimiterFound(tag_length
) => Some(Ok(tag_length
)),
787 // The end of the reader was found without finding the closing tag.
788 // Write the opening byte and captured text to the writer.
789 // Return 0 indicating that nothing was caputred but the reader still contains data.
790 DelimiterNotFound(not_tag_length
) => {
791 match w
.write(b
"{") {
792 Err(e
) => Some(Err(e
)),
794 match w
.write(&tag_buffer
.get_ref()[0..not_tag_length
]) {
795 Err(e
) => Some(Err(e
)),
802 ReadError(e
) | WriteError(e
) => Some(Err(e
)),
809 // Methods to write Parser help using templates.
811 /// Write help to stream for the parser in the format defined by the template.
813 /// Tags arg given inside curly brackets:
815 /// * `{bin}` - Binary name.
816 /// * `{version}` - Version number.
817 /// * `{author}` - Author information.
818 /// * `{usage}` - Automatically generated or given usage string.
819 /// * `{all-args}` - Help for all arguments (options, flags, positionals arguments,
820 /// and subcommands) including titles.
821 /// * `{unified}` - Unified help for options and flags.
822 /// * `{flags}` - Help for flags.
823 /// * `{options}` - Help for options.
824 /// * `{positionals}` - Help for positionals arguments.
825 /// * `{subcommands}` - Help for subcommands.
826 /// * `{after-help}` - Info to be displayed after the help message.
827 /// * `{before-help}` - Info to be displayed before the help message.
829 /// The template system is, on purpose, very simple. Therefore the tags have to writen
830 /// in the lowercase and without spacing.
831 fn write_templated_help(&mut self, parser
: &Parser
, template
: &str) -> ClapResult
<()> {
832 debugln
!("Help::write_templated_help;");
833 let mut tmplr
= Cursor
::new(&template
);
834 let mut tag_buf
= Cursor
::new(vec
![0u8; 15]);
836 // The strategy is to copy the template from the the reader to wrapped stream
837 // until a tag is found. Depending on its value, the appropriate content is copied
838 // to the wrapped stream.
839 // The copy from template is then resumed, repeating this sequence until reading
840 // the complete template.
843 let tag_length
= match copy_and_capture(&mut tmplr
, &mut self.writer
, &mut tag_buf
) {
844 None
=> return Ok(()),
845 Some(Err(e
)) => return Err(Error
::from(e
)),
846 Some(Ok(val
)) if val
> 0 => val
,
850 debugln
!("Help::write_template_help:iter: tag_buf={};", unsafe {
851 String
::from_utf8_unchecked(tag_buf
.get_ref()[0..tag_length
]
854 .collect
::<Vec
<_
>>())
856 match &tag_buf
.get_ref()[0..tag_length
] {
858 self.writer
.write_all(b
"Could not decode tag name")?
;
861 self.write_bin_name(parser
)?
;
866 parser
.meta
.version
.unwrap_or("unknown version"))?
;
871 parser
.meta
.author
.unwrap_or("unknown author"))?
;
876 parser
.meta
.about
.unwrap_or("unknown about"))?
;
881 parser
.meta
.long_about
.unwrap_or("unknown about"))?
;
884 write
!(self.writer
, "{}", usage
::create_usage_no_title(parser
, &[]))?
;
887 self.write_all_args(parser
)?
;
890 let opts_flags
= parser
893 .chain(parser
.opts().map(as_arg_trait
));
894 self.write_args(opts_flags
)?
;
897 self.write_args(parser
.flags().map(as_arg_trait
))?
;
900 self.write_args(parser
.opts().map(as_arg_trait
))?
;
903 self.write_args(parser
.positionals().map(as_arg_trait
))?
;
906 self.write_subcommands(parser
)?
;
911 parser
.meta
.more_help
.unwrap_or("unknown after-help"))?
;
916 parser
.meta
.pre_help
.unwrap_or("unknown before-help"))?
;
918 // Unknown tag, write it back.
920 self.writer
.write_all(b
"{")?
;
921 self.writer
.write_all(r
)?
;
922 self.writer
.write_all(b
"}")?
;
929 fn wrap_help(help
: &str, avail_chars
: usize) -> String
{
930 let wrapper
= textwrap
::Wrapper
::new(avail_chars
).break_words(false);
932 .map(|line
| wrapper
.fill(line
))
933 .collect
::<Vec
<String
>>()
939 use super::wrap_help
;
942 fn wrap_help_last_word() {
943 let help
= String
::from("foo bar baz");
944 assert_eq
!(wrap_help(&help
, 5), "foo\nbar\nbaz");