diff options
Diffstat (limited to 'clap/src/app/help.rs')
-rw-r--r-- | clap/src/app/help.rs | 1028 |
1 files changed, 0 insertions, 1028 deletions
diff --git a/clap/src/app/help.rs b/clap/src/app/help.rs deleted file mode 100644 index 34f97ac..0000000 --- a/clap/src/app/help.rs +++ /dev/null @@ -1,1028 +0,0 @@ -// Std -use std::borrow::Cow; -use std::cmp; -use std::collections::BTreeMap; -use std::fmt::Display; -use std::io::{self, Cursor, Read, Write}; -use std::usize; - -// Internal -use app::parser::Parser; -use app::usage; -use app::{App, AppSettings}; -use args::{AnyArg, ArgSettings, DispOrder}; -use errors::{Error, Result as ClapResult}; -use fmt::{Colorizer, ColorizerOption, Format}; -use map::VecMap; -use INTERNAL_ERROR_MSG; - -// Third Party -#[cfg(feature = "wrap_help")] -use term_size; -use textwrap; -use unicode_width::UnicodeWidthStr; - -#[cfg(not(feature = "wrap_help"))] -mod term_size { - pub fn dimensions() -> Option<(usize, usize)> { - None - } -} - -fn str_width(s: &str) -> usize { - UnicodeWidthStr::width(s) -} - -const TAB: &'static str = " "; - -// These are just convenient traits to make the code easier to read. -trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {} -impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T -where - T: AnyArg<'b, 'c> + Display, -{ -} - -trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder { - fn as_base(&self) -> &ArgWithDisplay<'b, 'c>; -} -impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T -where - T: ArgWithDisplay<'b, 'c> + DispOrder, -{ - fn as_base(&self) -> &ArgWithDisplay<'b, 'c> { - self - } -} - -fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> { - x -} - -impl<'b, 'c> DispOrder for App<'b, 'c> { - fn disp_ord(&self) -> usize { - 999 - } -} - -macro_rules! color { - ($_self:ident, $s:expr, $c:ident) => { - if $_self.color { - write!($_self.writer, "{}", $_self.cizer.$c($s)) - } else { - write!($_self.writer, "{}", $s) - } - }; - ($_self:ident, $fmt_s:expr, $v:expr, $c:ident) => { - if $_self.color { - write!($_self.writer, "{}", $_self.cizer.$c(format!($fmt_s, $v))) - } else { - write!($_self.writer, $fmt_s, $v) - } - }; -} - -/// `clap` Help Writer. -/// -/// Wraps a writer stream providing different methods to generate help for `clap` objects. -pub struct Help<'a> { - writer: &'a mut Write, - next_line_help: bool, - hide_pv: bool, - term_w: usize, - color: bool, - cizer: Colorizer, - longest: usize, - force_next_line: bool, - use_long: bool, -} - -// Public Functions -impl<'a> Help<'a> { - /// Create a new `Help` instance. - #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] - pub fn 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, - ) -> Self { - debugln!("Help::new;"); - Help { - writer: w, - next_line_help: next_line_help, - hide_pv: hide_pv, - term_w: match term_w { - Some(width) => if width == 0 { - usize::MAX - } else { - width - }, - None => cmp::min( - term_size::dimensions().map_or(120, |(w, _)| w), - match max_w { - None | Some(0) => usize::MAX, - Some(mw) => mw, - }, - ), - }, - color: color, - cizer: cizer, - longest: 0, - force_next_line: false, - use_long: use_long, - } - } - - /// Reads help settings from an App - /// and write its help to the wrapped stream. - pub fn write_app_help(w: &'a mut Write, app: &App, use_long: bool) -> ClapResult<()> { - debugln!("Help::write_app_help;"); - Self::write_parser_help(w, &app.p, use_long) - } - - /// Reads help settings from a Parser - /// and write its help to the wrapped stream. - pub fn write_parser_help(w: &'a mut Write, parser: &Parser, use_long: bool) -> ClapResult<()> { - debugln!("Help::write_parser_help;"); - Self::_write_parser_help(w, parser, false, use_long) - } - - /// Reads help settings from a Parser - /// and write its help to the wrapped stream which will be stderr. This method prevents - /// formatting when required. - pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> { - debugln!("Help::write_parser_help;"); - Self::_write_parser_help(w, parser, true, false) - } - - #[doc(hidden)] - pub fn _write_parser_help( - w: &'a mut Write, - parser: &Parser, - stderr: bool, - use_long: bool, - ) -> ClapResult<()> { - debugln!("Help::write_parser_help;"); - let nlh = parser.is_set(AppSettings::NextLineHelp); - let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp); - let color = parser.is_set(AppSettings::ColoredHelp); - let cizer = Colorizer::new(ColorizerOption { - use_stderr: stderr, - when: parser.color(), - }); - Self::new( - w, - nlh, - hide_v, - color, - cizer, - parser.meta.term_w, - parser.meta.max_w, - use_long, - ).write_help(parser) - } - - /// Writes the parser help to the wrapped stream. - pub fn write_help(&mut self, parser: &Parser) -> ClapResult<()> { - debugln!("Help::write_help;"); - if let Some(h) = parser.meta.help_str { - write!(self.writer, "{}", h).map_err(Error::from)?; - } else if let Some(tmpl) = parser.meta.template { - self.write_templated_help(parser, tmpl)?; - } else { - self.write_default_help(parser)?; - } - Ok(()) - } -} - -// Methods to write AnyArg help. -impl<'a> Help<'a> { - /// Writes help for each argument in the order they were declared to the wrapped stream. - fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()> - where - I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>, - { - debugln!("Help::write_args_unsorted;"); - // The shortest an arg can legally be is 2 (i.e. '-x') - self.longest = 2; - let mut arg_v = Vec::with_capacity(10); - let use_long = self.use_long; - for arg in args.filter(|arg| should_show_arg(use_long, *arg)) { - if arg.longest_filter() { - self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str())); - } - arg_v.push(arg) - } - let mut first = true; - for arg in arg_v { - if first { - first = false; - } else { - self.writer.write_all(b"\n")?; - } - self.write_arg(arg.as_base())?; - } - Ok(()) - } - - /// Sorts arguments by length and display order and write their help to the wrapped stream. - fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()> - where - I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>, - { - debugln!("Help::write_args;"); - // The shortest an arg can legally be is 2 (i.e. '-x') - self.longest = 2; - let mut ord_m = VecMap::new(); - let use_long = self.use_long; - // Determine the longest - for arg in args.filter(|arg| { - // If it's NextLineHelp, but we don't care to compute how long because it may be - // NextLineHelp on purpose *because* it's so long and would throw off all other - // args alignment - should_show_arg(use_long, *arg) - }) { - if arg.longest_filter() { - debugln!("Help::write_args: Current Longest...{}", self.longest); - self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str())); - debugln!("Help::write_args: New Longest...{}", self.longest); - } - let btm = ord_m.entry(arg.disp_ord()).or_insert(BTreeMap::new()); - btm.insert(arg.name(), arg); - } - let mut first = true; - for btm in ord_m.values() { - for arg in btm.values() { - if first { - first = false; - } else { - self.writer.write_all(b"\n")?; - } - self.write_arg(arg.as_base())?; - } - } - Ok(()) - } - - /// Writes help for an argument to the wrapped stream. - fn write_arg<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> { - debugln!("Help::write_arg;"); - self.short(arg)?; - self.long(arg)?; - let spec_vals = self.val(arg)?; - self.help(arg, &*spec_vals)?; - Ok(()) - } - - /// Writes argument's short command to the wrapped stream. - fn short<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> { - debugln!("Help::short;"); - write!(self.writer, "{}", TAB)?; - if let Some(s) = arg.short() { - color!(self, "-{}", s, good) - } else if arg.has_switch() { - write!(self.writer, "{}", TAB) - } else { - Ok(()) - } - } - - /// Writes argument's long command to the wrapped stream. - fn long<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> { - debugln!("Help::long;"); - if !arg.has_switch() { - return Ok(()); - } - if arg.takes_value() { - if let Some(l) = arg.long() { - if arg.short().is_some() { - write!(self.writer, ", ")?; - } - color!(self, "--{}", l, good)? - } - - let sep = if arg.is_set(ArgSettings::RequireEquals) { - "=" - } else { - " " - }; - write!(self.writer, "{}", sep)?; - } else if let Some(l) = arg.long() { - if arg.short().is_some() { - write!(self.writer, ", ")?; - } - color!(self, "--{}", l, good)?; - } - Ok(()) - } - - /// Writes argument's possible values to the wrapped stream. - fn val<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> Result<String, io::Error> { - debugln!("Help::val: arg={}", arg); - if arg.takes_value() { - let delim = if arg.is_set(ArgSettings::RequireDelimiter) { - arg.val_delim().expect(INTERNAL_ERROR_MSG) - } else { - ' ' - }; - if let Some(vec) = arg.val_names() { - let mut it = vec.iter().peekable(); - while let Some((_, val)) = it.next() { - color!(self, "<{}>", val, good)?; - if it.peek().is_some() { - write!(self.writer, "{}", delim)?; - } - } - let num = vec.len(); - if arg.is_set(ArgSettings::Multiple) && num == 1 { - color!(self, "...", good)?; - } - } else if let Some(num) = arg.num_vals() { - let mut it = (0..num).peekable(); - while let Some(_) = it.next() { - color!(self, "<{}>", arg.name(), good)?; - if it.peek().is_some() { - write!(self.writer, "{}", delim)?; - } - } - if arg.is_set(ArgSettings::Multiple) && num == 1 { - color!(self, "...", good)?; - } - } else if arg.has_switch() { - color!(self, "<{}>", arg.name(), good)?; - if arg.is_set(ArgSettings::Multiple) { - color!(self, "...", good)?; - } - } else { - color!(self, "{}", arg, good)?; - } - } - - let spec_vals = self.spec_vals(arg); - let h = arg.help().unwrap_or(""); - let h_w = str_width(h) + str_width(&*spec_vals); - let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp); - let taken = self.longest + 12; - self.force_next_line = !nlh && self.term_w >= taken - && (taken as f32 / self.term_w as f32) > 0.40 - && h_w > (self.term_w - taken); - - debug!("Help::val: Has switch..."); - if arg.has_switch() { - sdebugln!("Yes"); - debugln!("Help::val: force_next_line...{:?}", self.force_next_line); - debugln!("Help::val: nlh...{:?}", nlh); - debugln!("Help::val: taken...{}", taken); - debugln!( - "Help::val: help_width > (width - taken)...{} > ({} - {})", - h_w, - self.term_w, - taken - ); - debugln!("Help::val: longest...{}", self.longest); - debug!("Help::val: next_line..."); - if !(nlh || self.force_next_line) { - sdebugln!("No"); - let self_len = str_width(arg.to_string().as_str()); - // subtract ourself - let mut spcs = self.longest - self_len; - // Since we're writing spaces from the tab point we first need to know if we - // had a long and short, or just short - if arg.long().is_some() { - // Only account 4 after the val - spcs += 4; - } else { - // Only account for ', --' + 4 after the val - spcs += 8; - } - - write_nspaces!(self.writer, spcs); - } else { - sdebugln!("Yes"); - } - } else if !(nlh || self.force_next_line) { - sdebugln!("No, and not next_line"); - write_nspaces!( - self.writer, - self.longest + 4 - (str_width(arg.to_string().as_str())) - ); - } else { - sdebugln!("No"); - } - Ok(spec_vals) - } - - fn write_before_after_help(&mut self, h: &str) -> io::Result<()> { - debugln!("Help::write_before_after_help;"); - let mut help = String::from(h); - // determine if our help fits or needs to wrap - debugln!( - "Help::write_before_after_help: Term width...{}", - self.term_w - ); - let too_long = str_width(h) >= self.term_w; - - debug!("Help::write_before_after_help: Too long..."); - if too_long || h.contains("{n}") { - sdebugln!("Yes"); - debugln!("Help::write_before_after_help: help: {}", help); - debugln!( - "Help::write_before_after_help: help width: {}", - str_width(&*help) - ); - // Determine how many newlines we need to insert - debugln!( - "Help::write_before_after_help: Usable space: {}", - self.term_w - ); - help = wrap_help(&help.replace("{n}", "\n"), self.term_w); - } else { - sdebugln!("No"); - } - write!(self.writer, "{}", help)?; - Ok(()) - } - - /// Writes argument's help to the wrapped stream. - fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> { - debugln!("Help::help;"); - let h = if self.use_long && arg.name() != "" { - arg.long_help().unwrap_or_else(|| arg.help().unwrap_or("")) - } else { - arg.help().unwrap_or_else(|| arg.long_help().unwrap_or("")) - }; - let mut help = String::from(h) + spec_vals; - let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || (self.use_long && arg.name() != ""); - debugln!("Help::help: Next Line...{:?}", nlh); - - let spcs = if nlh || self.force_next_line { - 12 // "tab" * 3 - } else { - self.longest + 12 - }; - - let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= self.term_w; - - // Is help on next line, if so then indent - if nlh || self.force_next_line { - write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?; - } - - debug!("Help::help: Too long..."); - if too_long && spcs <= self.term_w || h.contains("{n}") { - sdebugln!("Yes"); - debugln!("Help::help: help...{}", help); - debugln!("Help::help: help width...{}", str_width(&*help)); - // Determine how many newlines we need to insert - let avail_chars = self.term_w - spcs; - debugln!("Help::help: Usable space...{}", avail_chars); - help = wrap_help(&help.replace("{n}", "\n"), avail_chars); - } else { - sdebugln!("No"); - } - if let Some(part) = help.lines().next() { - write!(self.writer, "{}", part)?; - } - for part in help.lines().skip(1) { - write!(self.writer, "\n")?; - if nlh || self.force_next_line { - write!(self.writer, "{}{}{}", TAB, TAB, TAB)?; - } else if arg.has_switch() { - write_nspaces!(self.writer, self.longest + 12); - } else { - write_nspaces!(self.writer, self.longest + 8); - } - write!(self.writer, "{}", part)?; - } - if !help.contains('\n') && (nlh || self.force_next_line) { - write!(self.writer, "\n")?; - } - Ok(()) - } - - fn spec_vals(&self, a: &ArgWithDisplay) -> String { - debugln!("Help::spec_vals: a={}", a); - let mut spec_vals = vec![]; - if let Some(ref env) = a.env() { - debugln!( - "Help::spec_vals: Found environment variable...[{:?}:{:?}]", - env.0, - env.1 - ); - let env_val = if !a.is_set(ArgSettings::HideEnvValues) { - format!( - "={}", - env.1.map_or(Cow::Borrowed(""), |val| val.to_string_lossy()) - ) - } else { - String::new() - }; - let env_info = format!(" [env: {}{}]", env.0.to_string_lossy(), env_val); - spec_vals.push(env_info); - } - if !a.is_set(ArgSettings::HideDefaultValue) { - if let Some(pv) = a.default_val() { - debugln!("Help::spec_vals: Found default value...[{:?}]", pv); - spec_vals.push(format!( - " [default: {}]", - if self.color { - self.cizer.good(pv.to_string_lossy()) - } else { - Format::None(pv.to_string_lossy()) - } - )); - } - } - if let Some(ref aliases) = a.aliases() { - debugln!("Help::spec_vals: Found aliases...{:?}", aliases); - spec_vals.push(format!( - " [aliases: {}]", - if self.color { - aliases - .iter() - .map(|v| format!("{}", self.cizer.good(v))) - .collect::<Vec<_>>() - .join(", ") - } else { - aliases.join(", ") - } - )); - } - if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) { - if let Some(pv) = a.possible_vals() { - debugln!("Help::spec_vals: Found possible vals...{:?}", pv); - spec_vals.push(if self.color { - format!( - " [possible values: {}]", - pv.iter() - .map(|v| format!("{}", self.cizer.good(v))) - .collect::<Vec<_>>() - .join(", ") - ) - } else { - format!(" [possible values: {}]", pv.join(", ")) - }); - } - } - spec_vals.join(" ") - } -} - -fn should_show_arg(use_long: bool, arg: &ArgWithOrder) -> bool { - if arg.is_set(ArgSettings::Hidden) { - return false; - } - - (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long) - || (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long) - || arg.is_set(ArgSettings::NextLineHelp) -} - -// Methods to write Parser help. -impl<'a> Help<'a> { - /// Writes help for all arguments (options, flags, args, subcommands) - /// including titles of a Parser Object to the wrapped stream. - #[cfg_attr(feature = "lints", allow(useless_let_if_seq))] - #[cfg_attr(feature = "cargo-clippy", allow(useless_let_if_seq))] - pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> { - debugln!("Help::write_all_args;"); - let flags = parser.has_flags(); - let pos = parser - .positionals() - .filter(|arg| !arg.is_set(ArgSettings::Hidden)) - .count() > 0; - let opts = parser.has_opts(); - let subcmds = parser.has_visible_subcommands(); - - let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage); - - let mut first = true; - - if unified_help && (flags || opts) { - let opts_flags = parser - .flags() - .map(as_arg_trait) - .chain(parser.opts().map(as_arg_trait)); - color!(self, "OPTIONS:\n", warning)?; - self.write_args(opts_flags)?; - first = false; - } else { - if flags { - color!(self, "FLAGS:\n", warning)?; - self.write_args(parser.flags().map(as_arg_trait))?; - first = false; - } - if opts { - if !first { - self.writer.write_all(b"\n\n")?; - } - color!(self, "OPTIONS:\n", warning)?; - self.write_args(parser.opts().map(as_arg_trait))?; - first = false; - } - } - - if pos { - if !first { - self.writer.write_all(b"\n\n")?; - } - color!(self, "ARGS:\n", warning)?; - self.write_args_unsorted(parser.positionals().map(as_arg_trait))?; - first = false; - } - - if subcmds { - if !first { - self.writer.write_all(b"\n\n")?; - } - color!(self, "SUBCOMMANDS:\n", warning)?; - self.write_subcommands(parser)?; - } - - Ok(()) - } - - /// Writes help for subcommands of a Parser Object to the wrapped stream. - fn write_subcommands(&mut self, parser: &Parser) -> io::Result<()> { - debugln!("Help::write_subcommands;"); - // The shortest an arg can legally be is 2 (i.e. '-x') - self.longest = 2; - let mut ord_m = VecMap::new(); - for sc in parser - .subcommands - .iter() - .filter(|s| !s.p.is_set(AppSettings::Hidden)) - { - let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new()); - self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str())); - //self.longest = cmp::max(self.longest, sc.p.meta.name.len()); - btm.insert(sc.p.meta.name.clone(), sc.clone()); - } - - let mut first = true; - for btm in ord_m.values() { - for sc in btm.values() { - if first { - first = false; - } else { - self.writer.write_all(b"\n")?; - } - self.write_arg(sc)?; - } - } - Ok(()) - } - - /// Writes version of a Parser Object to the wrapped stream. - fn write_version(&mut self, parser: &Parser) -> io::Result<()> { - debugln!("Help::write_version;"); - write!(self.writer, "{}", parser.meta.version.unwrap_or(""))?; - Ok(()) - } - - /// Writes binary name of a Parser Object to the wrapped stream. - fn write_bin_name(&mut self, parser: &Parser) -> io::Result<()> { - debugln!("Help::write_bin_name;"); - macro_rules! write_name { - () => {{ - let mut name = parser.meta.name.clone(); - name = name.replace("{n}", "\n"); - color!(self, wrap_help(&name, self.term_w), good)?; - }}; - } - if let Some(bn) = parser.meta.bin_name.as_ref() { - if bn.contains(' ') { - // Incase we're dealing with subcommands i.e. git mv is translated to git-mv - color!(self, bn.replace(" ", "-"), good)? - } else { - write_name!(); - } - } else { - write_name!(); - } - Ok(()) - } - - /// Writes default help for a Parser Object to the wrapped stream. - pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> { - debugln!("Help::write_default_help;"); - if let Some(h) = parser.meta.pre_help { - self.write_before_after_help(h)?; - self.writer.write_all(b"\n\n")?; - } - - macro_rules! write_thing { - ($thing:expr) => {{ - let mut owned_thing = $thing.to_owned(); - owned_thing = owned_thing.replace("{n}", "\n"); - write!(self.writer, "{}\n", wrap_help(&owned_thing, self.term_w))? - }}; - } - // Print the version - self.write_bin_name(parser)?; - self.writer.write_all(b" ")?; - self.write_version(parser)?; - self.writer.write_all(b"\n")?; - if let Some(author) = parser.meta.author { - write_thing!(author) - } - // if self.use_long { - // if let Some(about) = parser.meta.long_about { - // debugln!("Help::write_default_help: writing long about"); - // write_thing!(about) - // } else if let Some(about) = parser.meta.about { - // debugln!("Help::write_default_help: writing about"); - // write_thing!(about) - // } - // } else - if let Some(about) = parser.meta.long_about { - debugln!("Help::write_default_help: writing long about"); - write_thing!(about) - } else if let Some(about) = parser.meta.about { - debugln!("Help::write_default_help: writing about"); - write_thing!(about) - } - - color!(self, "\nUSAGE:", warning)?; - write!( - self.writer, - "\n{}{}\n\n", - TAB, - usage::create_usage_no_title(parser, &[]) - )?; - - let flags = parser.has_flags(); - let pos = parser.has_positionals(); - let opts = parser.has_opts(); - let subcmds = parser.has_subcommands(); - - if flags || opts || pos || subcmds { - self.write_all_args(parser)?; - } - - if let Some(h) = parser.meta.more_help { - if flags || opts || pos || subcmds { - self.writer.write_all(b"\n\n")?; - } - self.write_before_after_help(h)?; - } - - self.writer.flush().map_err(Error::from) - } -} - -/// Possible results for a copying function that stops when a given -/// byte was found. -enum CopyUntilResult { - DelimiterFound(usize), - DelimiterNotFound(usize), - ReaderEmpty, - ReadError(io::Error), - WriteError(io::Error), -} - -/// Copies the contents of a reader into a writer until a delimiter byte is found. -/// On success, the total number of bytes that were -/// copied from reader to writer is returned. -fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> CopyUntilResult { - debugln!("copy_until;"); - - let mut count = 0; - for wb in r.bytes() { - match wb { - Ok(b) => { - if b == delimiter_byte { - return CopyUntilResult::DelimiterFound(count); - } - match w.write(&[b]) { - Ok(c) => count += c, - Err(e) => return CopyUntilResult::WriteError(e), - } - } - Err(e) => return CopyUntilResult::ReadError(e), - } - } - if count > 0 { - CopyUntilResult::DelimiterNotFound(count) - } else { - CopyUntilResult::ReaderEmpty - } -} - -/// Copies the contents of a reader into a writer until a {tag} is found, -/// copying the tag content to a buffer and returning its size. -/// In addition to errors, there are three possible outputs: -/// - `None`: The reader was consumed. -/// - `Some(Ok(0))`: No tag was captured but the reader still contains data. -/// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`. -fn copy_and_capture<R: Read, W: Write>( - r: &mut R, - w: &mut W, - tag_buffer: &mut Cursor<Vec<u8>>, -) -> Option<io::Result<usize>> { - use self::CopyUntilResult::*; - debugln!("copy_and_capture;"); - - // Find the opening byte. - match copy_until(r, w, b'{') { - // The end of the reader was reached without finding the opening tag. - // (either with or without having copied data to the writer) - // Return None indicating that we are done. - ReaderEmpty | DelimiterNotFound(_) => None, - - // Something went wrong. - ReadError(e) | WriteError(e) => Some(Err(e)), - - // The opening byte was found. - // (either with or without having copied data to the writer) - DelimiterFound(_) => { - // Lets reset the buffer first and find out how long it is. - tag_buffer.set_position(0); - let buffer_size = tag_buffer.get_ref().len(); - - // Find the closing byte,limiting the reader to the length of the buffer. - let mut rb = r.take(buffer_size as u64); - match copy_until(&mut rb, tag_buffer, b'}') { - // We were already at the end of the reader. - // Return None indicating that we are done. - ReaderEmpty => None, - - // The closing tag was found. - // Return the tag_length. - DelimiterFound(tag_length) => Some(Ok(tag_length)), - - // The end of the reader was found without finding the closing tag. - // Write the opening byte and captured text to the writer. - // Return 0 indicating that nothing was captured but the reader still contains data. - DelimiterNotFound(not_tag_length) => match w.write(b"{") { - Err(e) => Some(Err(e)), - _ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) { - Err(e) => Some(Err(e)), - _ => Some(Ok(0)), - }, - }, - - ReadError(e) | WriteError(e) => Some(Err(e)), - } - } - } -} - -// Methods to write Parser help using templates. -impl<'a> Help<'a> { - /// Write help to stream for the parser in the format defined by the template. - /// - /// Tags arg given inside curly brackets: - /// Valid tags are: - /// * `{bin}` - Binary name. - /// * `{version}` - Version number. - /// * `{author}` - Author information. - /// * `{usage}` - Automatically generated or given usage string. - /// * `{all-args}` - Help for all arguments (options, flags, positionals arguments, - /// and subcommands) including titles. - /// * `{unified}` - Unified help for options and flags. - /// * `{flags}` - Help for flags. - /// * `{options}` - Help for options. - /// * `{positionals}` - Help for positionals arguments. - /// * `{subcommands}` - Help for subcommands. - /// * `{after-help}` - Info to be displayed after the help message. - /// * `{before-help}` - Info to be displayed before the help message. - /// - /// The template system is, on purpose, very simple. Therefore the tags have to written - /// in the lowercase and without spacing. - fn write_templated_help(&mut self, parser: &Parser, template: &str) -> ClapResult<()> { - debugln!("Help::write_templated_help;"); - let mut tmplr = Cursor::new(&template); - let mut tag_buf = Cursor::new(vec![0u8; 15]); - - // The strategy is to copy the template from the reader to wrapped stream - // until a tag is found. Depending on its value, the appropriate content is copied - // to the wrapped stream. - // The copy from template is then resumed, repeating this sequence until reading - // the complete template. - - loop { - let tag_length = match copy_and_capture(&mut tmplr, &mut self.writer, &mut tag_buf) { - None => return Ok(()), - Some(Err(e)) => return Err(Error::from(e)), - Some(Ok(val)) if val > 0 => val, - _ => continue, - }; - - debugln!("Help::write_template_help:iter: tag_buf={};", unsafe { - String::from_utf8_unchecked( - tag_buf.get_ref()[0..tag_length] - .iter() - .map(|&i| i) - .collect::<Vec<_>>(), - ) - }); - match &tag_buf.get_ref()[0..tag_length] { - b"?" => { - self.writer.write_all(b"Could not decode tag name")?; - } - b"bin" => { - self.write_bin_name(parser)?; - } - b"version" => { - write!( - self.writer, - "{}", - parser.meta.version.unwrap_or("unknown version") - )?; - } - b"author" => { - write!( - self.writer, - "{}", - parser.meta.author.unwrap_or("unknown author") - )?; - } - b"about" => { - write!( - self.writer, - "{}", - parser.meta.about.unwrap_or("unknown about") - )?; - } - b"long-about" => { - write!( - self.writer, - "{}", - parser.meta.long_about.unwrap_or("unknown about") - )?; - } - b"usage" => { - write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?; - } - b"all-args" => { - self.write_all_args(parser)?; - } - b"unified" => { - let opts_flags = parser - .flags() - .map(as_arg_trait) - .chain(parser.opts().map(as_arg_trait)); - self.write_args(opts_flags)?; - } - b"flags" => { - self.write_args(parser.flags().map(as_arg_trait))?; - } - b"options" => { - self.write_args(parser.opts().map(as_arg_trait))?; - } - b"positionals" => { - self.write_args(parser.positionals().map(as_arg_trait))?; - } - b"subcommands" => { - self.write_subcommands(parser)?; - } - b"after-help" => { - write!( - self.writer, - "{}", - parser.meta.more_help.unwrap_or("unknown after-help") - )?; - } - b"before-help" => { - write!( - self.writer, - "{}", - parser.meta.pre_help.unwrap_or("unknown before-help") - )?; - } - // Unknown tag, write it back. - r => { - self.writer.write_all(b"{")?; - self.writer.write_all(r)?; - self.writer.write_all(b"}")?; - } - } - } - } -} - -fn wrap_help(help: &str, avail_chars: usize) -> String { - let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false); - help.lines() - .map(|line| wrapper.fill(line)) - .collect::<Vec<String>>() - .join("\n") -} - -#[cfg(test)] -mod test { - use super::wrap_help; - - #[test] - fn wrap_help_last_word() { - let help = String::from("foo bar baz"); - assert_eq!(wrap_help(&help, 5), "foo\nbar\nbaz"); - } -} |