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