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