7 io
::{self, Cursor, Read, Write}
,
12 #[cfg(feature = "wrap_help")]
14 #[cfg(feature = "wrap_help")]
16 use unicode_width
::UnicodeWidthStr
;
20 app
::{parser::Parser, usage, App, AppSettings}
,
21 args
::{AnyArg, ArgSettings, DispOrder}
,
22 errors
::{Error, Result as ClapResult}
,
23 fmt
::{Colorizer, ColorizerOption, Format}
,
28 #[cfg(not(feature = "wrap_help"))]
30 pub fn dimensions() -> Option
<(usize, usize)> {
35 fn str_width(s
: &str) -> usize {
36 UnicodeWidthStr
::width(s
)
39 const TAB
: &str = " ";
41 // These are just convenient traits to make the code easier to read.
42 trait ArgWithDisplay
<'b
, 'c
>: AnyArg
<'b
, 'c
> + Display {}
43 impl<'b
, 'c
, T
> ArgWithDisplay
<'b
, 'c
> for T
where T
: AnyArg
<'b
, 'c
> + Display {}
45 trait ArgWithOrder
<'b
, 'c
>: ArgWithDisplay
<'b
, 'c
> + DispOrder
{
46 fn as_base(&self) -> &ArgWithDisplay
<'b
, 'c
>;
48 impl<'b
, 'c
, T
> ArgWithOrder
<'b
, 'c
> for T
50 T
: ArgWithDisplay
<'b
, 'c
> + DispOrder
,
52 fn as_base(&self) -> &ArgWithDisplay
<'b
, 'c
> {
57 fn as_arg_trait
<'a
, 'b
, T
: ArgWithOrder
<'a
, 'b
>>(x
: &T
) -> &ArgWithOrder
<'a
, 'b
> {
61 impl<'b
, 'c
> DispOrder
for App
<'b
, 'c
> {
62 fn disp_ord(&self) -> usize {
68 ($_self
:ident
, $s
:expr
, $c
:ident
) => {
70 write
!($_self
.writer
, "{}", $_self
.cizer
.$
c($s
))
72 write
!($_self
.writer
, "{}", $s
)
75 ($_self
:ident
, $fmt_s
:expr
, $v
:expr
, $c
:ident
) => {
77 write
!($_self
.writer
, "{}", $_self
.cizer
.$
c(format
!($fmt_s
, $v
)))
79 write
!($_self
.writer
, $fmt_s
, $v
)
84 /// `clap` Help Writer.
86 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
88 writer
: &'a
mut Write
,
95 force_next_line
: bool
,
101 /// Create a new `Help` instance.
102 #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
105 next_line_help
: bool
,
109 term_w
: Option
<usize>,
110 max_w
: Option
<usize>,
113 debugln
!("Help::new;");
118 term_w
: match term_w
{
127 term_size
::dimensions().map_or(120, |(w
, _
)| w
),
129 None
| Some(0) => usize::MAX
,
137 force_next_line
: false,
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
)
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
)
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)
165 pub fn _write_parser_help(
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
{
177 when
: parser
.color(),
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
)?
;
200 self.write_default_help(parser
)?
;
206 // Methods to write AnyArg help.
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
<()>
211 I
: Iterator
<Item
= &'d ArgWithOrder
<'b
, 'c
>>,
213 debugln
!("Help::write_args_unsorted;");
214 // The shortest an arg can legally be is 2 (i.e. '-x')
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()));
224 let mut first
= true;
229 self.writer
.write_all(b
"\n")?
;
231 self.write_arg(arg
.as_base())?
;
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
<()>
239 I
: Iterator
<Item
= &'d ArgWithOrder
<'b
, 'c
>>,
241 debugln
!("Help::write_args;");
242 // The shortest an arg can legally be is 2 (i.e. '-x')
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
251 should_show_arg(use_long
, *arg
)
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
);
258 let btm
= ord_m
.entry(arg
.disp_ord()).or_insert(BTreeMap
::new());
259 btm
.insert(arg
.name(), arg
);
261 let mut first
= true;
262 for btm
in ord_m
.values() {
263 for arg
in btm
.values() {
267 self.writer
.write_all(b
"\n")?
;
269 self.write_arg(arg
.as_base())?
;
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;");
280 let spec_vals
= self.val(arg
)?
;
281 self.help(arg
, &*spec_vals
)?
;
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
)
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() {
304 if arg
.takes_value() {
305 if let Some(l
) = arg
.long() {
306 if arg
.short().is_some() {
307 write
!(self.writer
, ", ")?
;
309 color
!(self, "--{}", l
, good
)?
312 let sep
= if arg
.is_set(ArgSettings
::RequireEquals
) {
317 write
!(self.writer
, "{}", sep
)?
;
318 } else if let Some(l
) = arg
.long() {
319 if arg
.short().is_some() {
320 write
!(self.writer
, ", ")?
;
322 color
!(self, "--{}", l
, good
)?
;
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
)
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
)?
;
345 if arg
.is_set(ArgSettings
::Multiple
) && num
== 1 {
346 color
!(self, "...", good
)?
;
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
)?
;
356 if arg
.is_set(ArgSettings
::Multiple
) && num
== 1 {
357 color
!(self, "...", good
)?
;
359 } else if arg
.has_switch() {
360 color
!(self, "<{}>", arg
.name(), good
)?
;
361 if arg
.is_set(ArgSettings
::Multiple
) {
362 color
!(self, "...", good
)?
;
365 color
!(self, "{}", arg
, good
)?
;
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
);
379 debug
!("Help::val: Has switch...");
380 if arg
.has_switch() {
382 debugln
!("Help::val: force_next_line...{:?}", self.force_next_line
);
383 debugln
!("Help::val: nlh...{:?}", nlh
);
384 debugln
!("Help::val: taken...{}", taken
);
386 "Help::val: help_width > (width - taken)...{} > ({} - {})",
391 debugln
!("Help::val: longest...{}", self.longest
);
392 debug
!("Help::val: next_line...");
393 if !(nlh
|| self.force_next_line
) {
395 let self_len
= str_width(arg
.to_string().as_str());
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
404 // Only account for ', --' + 4 after the val
408 write_nspaces
!(self.writer
, spcs
);
412 } else if !(nlh
|| self.force_next_line
) {
413 sdebugln
!("No, and not next_line");
416 self.longest
+ 4 - (str_width(arg
.to_string().as_str()))
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
429 "Help::write_before_after_help: Term width...{}",
432 let too_long
= str_width(h
) >= self.term_w
;
434 debug
!("Help::write_before_after_help: Too long...");
435 if too_long
|| h
.contains("{n}") {
437 debugln
!("Help::write_before_after_help: help: {}", help
);
439 "Help::write_before_after_help: help width: {}",
442 // Determine how many newlines we need to insert
444 "Help::write_before_after_help: Usable space: {}",
447 help
= wrap_help(&help
.replace("{n}", "\n"), self.term_w
);
451 write
!(self.writer
, "{}", help
)?
;
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(""))
461 arg
.help().unwrap_or_else(|| arg
.long_help().unwrap_or(""))
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
);
469 let spcs
= if nlh
|| self.force_next_line
{
475 let too_long
= spcs
+ str_width(h
) + str_width(&*spec_vals
) >= self.term_w
;
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
)?
;
482 debug
!("Help::help: Too long...");
483 if too_long
&& spcs
<= self.term_w
|| h
.contains("{n}") {
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
);
494 if let Some(part
) = help
.lines().next() {
495 write
!(self.writer
, "{}", part
)?
;
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);
504 write_nspaces
!(self.writer
, self.longest
+ 8);
506 write
!(self.writer
, "{}", part
)?
;
508 if !help
.contains('
\n'
) && (nlh
|| self.force_next_line
) {
509 writeln
!(self.writer
)?
;
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() {
519 "Help::spec_vals: Found environment variable...[{:?}:{:?}]",
523 let env_val
= if !a
.is_set(ArgSettings
::HideEnvValues
) {
526 env
.1.map_or(Cow
::Borrowed(""), |val
| val
.to_string_lossy())
531 let env_info
= format
!(" [env: {}{}]", env
.0.to_string_lossy(), env_val
);
532 spec_vals
.push(env_info
);
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
!(
540 self.cizer
.good(pv
.to_string_lossy())
542 Format
::None(pv
.to_string_lossy())
547 if let Some(ref aliases
) = a
.aliases() {
548 debugln
!("Help::spec_vals: Found aliases...{:?}", aliases
);
549 spec_vals
.push(format
!(
554 .map(|v
| format
!("{}", self.cizer
.good(v
)))
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
{
567 " [possible values: {}]",
569 .map(|v
| format
!("{}", self.cizer
.good(v
)))
574 format
!(" [possible values: {}]", pv
.join(", "))
582 fn should_show_arg(use_long
: bool
, arg
: &ArgWithOrder
) -> bool
{
583 if arg
.is_set(ArgSettings
::Hidden
) {
587 (!arg
.is_set(ArgSettings
::HiddenLongHelp
) && use_long
)
588 || (!arg
.is_set(ArgSettings
::HiddenShortHelp
) && !use_long
)
589 || arg
.is_set(ArgSettings
::NextLineHelp
)
592 // Methods to write Parser help.
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();
601 .filter(|arg
| !arg
.is_set(ArgSettings
::Hidden
))
604 let opts
= parser
.has_opts();
605 let subcmds
= parser
.has_visible_subcommands();
607 let unified_help
= parser
.is_set(AppSettings
::UnifiedHelpMessage
);
609 let mut first
= true;
611 if unified_help
&& (flags
|| opts
) {
612 let opts_flags
= parser
615 .chain(parser
.opts().map(as_arg_trait
));
616 color
!(self, "OPTIONS:\n", warning
)?
;
617 self.write_args(opts_flags
)?
;
621 color
!(self, "FLAGS:\n", warning
)?
;
622 self.write_args(parser
.flags().map(as_arg_trait
))?
;
627 self.writer
.write_all(b
"\n\n")?
;
629 color
!(self, "OPTIONS:\n", warning
)?
;
630 self.write_args(parser
.opts().map(as_arg_trait
))?
;
637 self.writer
.write_all(b
"\n\n")?
;
639 color
!(self, "ARGS:\n", warning
)?
;
640 self.write_args_unsorted(parser
.positionals().map(as_arg_trait
))?
;
646 self.writer
.write_all(b
"\n\n")?
;
648 color
!(self, "SUBCOMMANDS:\n", warning
)?
;
649 self.write_subcommands(parser
)?
;
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')
660 let mut ord_m
= VecMap
::new();
664 .filter(|s
| !s
.p
.is_set(AppSettings
::Hidden
))
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());
672 let mut first
= true;
673 for btm
in ord_m
.values() {
674 for sc
in btm
.values() {
678 self.writer
.write_all(b
"\n")?
;
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(""))?
;
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
{
698 let mut name
= parser
.meta
.name
.clone();
699 name
= name
.replace("{n}", "\n");
700 color
!(self, wrap_help(&name
, self.term_w
), good
)?
;
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
)?
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")?
;
724 macro_rules
! write_thing
{
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
))?
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
{
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)
748 if let Some(about
) = parser
.meta
.long_about
{
749 debugln
!("Help::write_default_help: writing long about");
751 } else if let Some(about
) = parser
.meta
.about
{
752 debugln
!("Help::write_default_help: writing about");
756 color
!(self, "\nUSAGE:", warning
)?
;
761 usage
::create_usage_no_title(parser
, &[])
764 let flags
= parser
.has_flags();
765 let pos
= parser
.has_positionals();
766 let opts
= parser
.has_opts();
767 let subcmds
= parser
.has_subcommands();
769 if flags
|| opts
|| pos
|| subcmds
{
770 self.write_all_args(parser
)?
;
773 if let Some(h
) = parser
.meta
.more_help
{
774 if flags
|| opts
|| pos
|| subcmds
{
775 self.writer
.write_all(b
"\n\n")?
;
777 self.write_before_after_help(h
)?
;
780 self.writer
.flush().map_err(Error
::from
)
784 /// Possible results for a copying function that stops when a given
786 enum CopyUntilResult
{
787 DelimiterFound(usize),
788 DelimiterNotFound(usize),
790 ReadError(io
::Error
),
791 WriteError(io
::Error
),
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.
797 fn copy_until
<R
: Read
, W
: Write
>(r
: &mut R
, w
: &mut W
, delimiter_byte
: u8) -> CopyUntilResult
{
798 debugln
!("copy_until;");
801 for wb
in r
.bytes() {
804 if b
== delimiter_byte
{
805 return CopyUntilResult
::DelimiterFound(count
);
807 match w
.write(&[b
]) {
809 Err(e
) => return CopyUntilResult
::WriteError(e
),
812 Err(e
) => return CopyUntilResult
::ReadError(e
),
816 CopyUntilResult
::DelimiterNotFound(count
)
818 CopyUntilResult
::ReaderEmpty
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`.
828 fn copy_and_capture
<R
: Read
, W
: Write
>(
831 tag_buffer
: &mut Cursor
<Vec
<u8>>,
832 ) -> Option
<io
::Result
<usize>> {
833 use self::CopyUntilResult
::*;
834 debugln
!("copy_and_capture;");
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
,
843 // Something went wrong.
844 ReadError(e
) | WriteError(e
) => Some(Err(e
)),
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();
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.
860 // The closing tag was found.
861 // Return the tag_length.
862 DelimiterFound(tag_length
) => Some(Ok(tag_length
)),
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
)),
875 ReadError(e
) | WriteError(e
) => Some(Err(e
)),
881 // Methods to write Parser help using templates.
883 /// Write help to stream for the parser in the format defined by the template.
885 /// Tags arg given inside curly brackets:
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.
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]);
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.
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
,
922 debugln
!("Help::write_template_help:iter: tag_buf={};", unsafe {
923 String
::from_utf8_unchecked(
924 tag_buf
.get_ref()[0..tag_length
]
927 .collect
::<Vec
<_
>>(),
930 match &tag_buf
.get_ref()[0..tag_length
] {
932 self.writer
.write_all(b
"Could not decode tag name")?
;
935 self.write_bin_name(parser
)?
;
941 parser
.meta
.version
.unwrap_or("unknown version")
948 parser
.meta
.author
.unwrap_or("unknown author")
955 parser
.meta
.about
.unwrap_or("unknown about")
962 parser
.meta
.long_about
.unwrap_or("unknown about")
966 write
!(self.writer
, "{}", usage
::create_usage_no_title(parser
, &[]))?
;
969 self.write_all_args(parser
)?
;
972 let opts_flags
= parser
975 .chain(parser
.opts().map(as_arg_trait
));
976 self.write_args(opts_flags
)?
;
979 self.write_args(parser
.flags().map(as_arg_trait
))?
;
982 self.write_args(parser
.opts().map(as_arg_trait
))?
;
985 self.write_args(parser
.positionals().map(as_arg_trait
))?
;
988 self.write_subcommands(parser
)?
;
994 parser
.meta
.more_help
.unwrap_or("unknown after-help")
1001 parser
.meta
.pre_help
.unwrap_or("unknown before-help")
1004 // Unknown tag, write it back.
1006 self.writer
.write_all(b
"{")?
;
1007 self.writer
.write_all(r
)?
;
1008 self.writer
.write_all(b
"}")?
;
1015 fn wrap_help(help
: &str, avail_chars
: usize) -> String
{
1016 let wrapper
= textwrap
::Wrapper
::new(avail_chars
).break_words(false);
1018 .map(|line
| wrapper
.fill(line
))
1019 .collect
::<Vec
<String
>>()
1025 use super::wrap_help
;
1028 fn wrap_help_last_word() {
1029 let help
= String
::from("foo bar baz");
1030 assert_eq
!(wrap_help(&help
, 5), "foo\nbar\nbaz");