use std::borrow::Cow;
use std::cmp;
use std::collections::BTreeMap;
use std::io::{self, Cursor, Read, Write};
use std::usize;
use crate::build::{App, AppSettings, Arg, ArgSettings};
use crate::output::fmt::{Colorizer, ColorizerOption, Format};
use crate::output::Usage;
use crate::parse::errors::{Error, Result as ClapResult};
use crate::parse::Parser;
use crate::util::VecMap;
use crate::INTERNAL_ERROR_MSG;
use unicode_width::UnicodeWidthStr;
#[cfg(not(feature = "wrap_help"))]
mod term_size {
pub(crate) fn dimensions() -> Option<(usize, usize)> {
None
}
}
fn str_width(s: &str) -> usize {
UnicodeWidthStr::width(s)
}
const TAB: &str = " ";
pub(crate) struct Help<'b, 'c, 'd, 'w> {
writer: &'w mut dyn Write,
parser: &'d Parser<'b, 'c>,
next_line_help: bool,
hide_pv: bool,
term_w: usize,
color: bool,
cizer: Colorizer,
longest: usize,
force_next_line: bool,
use_long: bool,
}
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
pub(crate) fn new(
w: &'w mut dyn Write,
parser: &'d Parser<'b, 'c>,
use_long: bool,
stderr: bool,
) -> Self {
debugln!("Help::new;");
let term_w = match parser.app.term_w {
Some(0) => usize::MAX,
Some(w) => w,
None => cmp::min(
term_size::dimensions().map_or(120, |(w, _)| w),
match parser.app.max_w {
None | Some(0) => usize::MAX,
Some(mw) => mw,
},
),
};
let nlh = parser.is_set(AppSettings::NextLineHelp);
let hide_pv = parser.is_set(AppSettings::HidePossibleValuesInHelp);
let color = parser.is_set(AppSettings::ColoredHelp);
let cizer = Colorizer::new(&ColorizerOption {
use_stderr: stderr,
when: parser.app.color(),
});
Help {
writer: w,
parser,
next_line_help: nlh,
hide_pv,
term_w,
color,
longest: 0,
force_next_line: false,
cizer,
use_long,
}
}
pub(crate) fn write_help(&mut self) -> ClapResult<()> {
debugln!("Help::write_help;");
if let Some(h) = self.parser.app.help_str {
write!(self.writer, "{}", h).map_err(Error::from)?;
} else if let Some(tmpl) = self.parser.app.template {
self.write_templated_help(tmpl)?;
} else {
self.write_default_help()?;
}
writeln!(self.writer)?;
Ok(())
}
}
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
fn color(&mut self, f: Format<&str>) -> io::Result<()> {
match f {
Format::Good(g) => {
if self.color {
write!(self.writer, "{}", self.cizer.good(g))
} else {
write!(self.writer, "{}", g)
}
}
Format::Warning(w) => {
if self.color {
write!(self.writer, "{}", self.cizer.warning(w))
} else {
write!(self.writer, "{}", w)
}
}
Format::Error(e) => {
if self.color {
write!(self.writer, "{}", self.cizer.error(e))
} else {
write!(self.writer, "{}", e)
}
}
_ => unreachable!(),
}
}
fn write_args_unsorted(&mut self, args: &[&Arg<'b>]) -> io::Result<()> {
debugln!("Help::write_args_unsorted;");
self.longest = 2;
let mut arg_v = Vec::with_capacity(10);
let use_long = self.use_long;
for arg in args.iter().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;
let arg_c = arg_v.len();
for (i, arg) in arg_v.iter().enumerate() {
if first {
first = false;
} else {
self.writer.write_all(b"\n")?;
}
self.write_arg(arg, i < arg_c)?;
}
Ok(())
}
fn write_args(&mut self, args: &[&Arg<'b>]) -> io::Result<()> {
debugln!("Help::write_args;");
self.longest = 2;
let mut ord_m = VecMap::new();
let use_long = self.use_long;
for arg in args.iter().filter(|arg| {
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, false)?;
}
}
Ok(())
}
fn write_arg(&mut self, arg: &Arg<'c>, prevent_nlh: bool) -> io::Result<()> {
debugln!("Help::write_arg;");
self.short(arg)?;
self.long(arg)?;
let spec_vals = self.val(arg)?;
self.help(arg, &*spec_vals, prevent_nlh)?;
Ok(())
}
fn short(&mut self, arg: &Arg<'c>) -> io::Result<()> {
debugln!("Help::short;");
write!(self.writer, "{}", TAB)?;
if let Some(s) = arg.short {
self.color(Format::Good(&*format!("-{}", s)))
} else if arg.has_switch() {
write!(self.writer, "{}", TAB)
} else {
Ok(())
}
}
fn long(&mut self, arg: &Arg<'c>) -> io::Result<()> {
debugln!("Help::long;");
if !arg.has_switch() {
return Ok(());
}
if arg.is_set(ArgSettings::TakesValue) {
if let Some(l) = arg.long {
if arg.short.is_some() {
write!(self.writer, ", ")?;
}
self.color(Format::Good(&*format!("--{}", l)))?
}
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, ", ")?;
}
self.color(Format::Good(&*format!("--{}", l)))?;
}
Ok(())
}
fn val(&mut self, arg: &Arg<'c>) -> Result<String, io::Error> {
debugln!("Help::val: arg={}", arg.name);
let mult =
arg.is_set(ArgSettings::MultipleValues) || arg.is_set(ArgSettings::MultipleOccurrences);
if arg.is_set(ArgSettings::TakesValue) || arg.index.is_some() {
let delim = if arg.is_set(ArgSettings::RequireDelimiter) {
arg.val_delim.expect(INTERNAL_ERROR_MSG)
} else {
' '
};
if let Some(ref vec) = arg.val_names {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
self.color(Format::Good(&*format!("<{}>", val)))?;
if it.peek().is_some() {
write!(self.writer, "{}", delim)?;
}
}
let num = vec.len();
if mult && num == 1 {
self.color(Format::Good("..."))?;
}
} else if let Some(num) = arg.num_vals {
let mut it = (0..num).peekable();
while let Some(_) = it.next() {
self.color(Format::Good(&*format!("<{}>", arg.name)))?;
if it.peek().is_some() {
write!(self.writer, "{}", delim)?;
}
}
if mult && num == 1 {
self.color(Format::Good("..."))?;
}
} else if arg.has_switch() {
self.color(Format::Good(&*format!("<{}>", arg.name)))?;
if mult {
self.color(Format::Good("..."))?;
}
} else {
self.color(Format::Good(&*arg.to_string()))?;
}
}
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());
let mut spcs = self.longest - self_len;
if arg.long.is_some() {
spcs += 4;
} else {
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);
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)
);
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(())
}
fn help(&mut self, arg: &Arg<'c>, spec_vals: &str, prevent_nlh: bool) -> io::Result<()> {
debugln!("Help::help;");
let h = if self.use_long {
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;
debugln!("Help::help: Next Line...{:?}", nlh);
let spcs = if nlh || self.force_next_line {
12 } else {
self.longest + 12
};
let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= self.term_w;
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));
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) {
writeln!(self.writer)?;
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 !prevent_nlh && !help.contains('\n') && (nlh || self.force_next_line) {
writeln!(self.writer)?;
}
Ok(())
}
fn spec_vals(&self, a: &Arg) -> 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
.as_ref()
.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(ref pv) = a.default_vals {
debugln!("Help::spec_vals: Found default value...[{:?}]", pv);
let pvs = if self.color {
pv.iter()
.map(|&pvs| format!("{}", self.cizer.good(pvs.to_string_lossy())))
.collect::<Vec<_>>()
.join(" ")
} else {
pv.iter()
.map(|&pvs| format!("{}", Format::None(pvs.to_string_lossy())))
.collect::<Vec<_>>()
.join(" ")
};
spec_vals.push(format!(" [default: {}]", pvs));
}
}
if let Some(ref aliases) = a.aliases {
debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
let als = if self.color {
aliases
.iter()
.filter(|&als| als.1) .map(|&als| format!("{}", self.cizer.good(als.0))) .collect::<Vec<_>>()
.join(", ")
} else {
aliases
.iter()
.filter(|&als| als.1)
.map(|&als| als.0)
.collect::<Vec<_>>()
.join(", ")
};
if !als.is_empty() {
spec_vals.push(format!(" [aliases: {}]", als));
}
}
if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) {
if let Some(ref 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(" ")
}
}
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
fn write_subcommand(&mut self, app: &App<'b>) -> io::Result<()> {
debugln!("Help::write_subcommand;");
write!(self.writer, "{}", TAB)?;
self.color(Format::Good(&*app.name))?;
let spec_vals = self.sc_val(app)?;
self.sc_help(app, &*spec_vals)?;
Ok(())
}
fn sc_val(&mut self, app: &App<'b>) -> Result<String, io::Error> {
debugln!("Help::sc_val: app={}", app.name);
let spec_vals = self.sc_spec_vals(app);
let h = app.about.unwrap_or("");
let h_w = str_width(h) + str_width(&*spec_vals);
let nlh = self.next_line_help;
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);
if !(nlh || self.force_next_line) {
write_nspaces!(
self.writer,
self.longest + 4 - (str_width(app.to_string().as_str()))
);
}
Ok(spec_vals)
}
fn sc_spec_vals(&self, a: &App) -> String {
debugln!("Help::sc_spec_vals: a={}", a.name);
let mut spec_vals = vec![];
if let Some(ref aliases) = a.aliases {
debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
let als = if self.color {
aliases
.iter()
.filter(|&als| als.1) .map(|&als| format!("{}", self.cizer.good(als.0))) .collect::<Vec<_>>()
.join(", ")
} else {
aliases
.iter()
.filter(|&als| als.1)
.map(|&als| als.0)
.collect::<Vec<_>>()
.join(", ")
};
if !als.is_empty() {
spec_vals.push(format!(" [aliases: {}]", als));
}
}
spec_vals.join(" ")
}
fn sc_help(&mut self, app: &App<'b>, spec_vals: &str) -> io::Result<()> {
debugln!("Help::sc_help;");
let h = if self.use_long {
app.long_about.unwrap_or_else(|| app.about.unwrap_or(""))
} else {
app.about.unwrap_or_else(|| app.long_about.unwrap_or(""))
};
let mut help = String::from(h) + spec_vals;
let nlh = self.next_line_help || self.use_long;
debugln!("Help::sc_help: Next Line...{:?}", nlh);
let spcs = if nlh || self.force_next_line {
12 } else {
self.longest + 12
};
let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= self.term_w;
if nlh || self.force_next_line {
write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?;
}
debug!("Help::sc_help: Too long...");
if too_long && spcs <= self.term_w || h.contains("{n}") {
sdebugln!("Yes");
debugln!("Help::sc_help: help...{}", help);
debugln!("Help::sc_help: help width...{}", str_width(&*help));
let avail_chars = self.term_w - spcs;
debugln!("Help::sc_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) {
writeln!(self.writer)?;
if nlh || self.force_next_line {
write!(self.writer, "{}{}{}", TAB, TAB, TAB)?;
} else {
write_nspaces!(self.writer, self.longest + 8);
}
write!(self.writer, "{}", part)?;
}
if !help.contains('\n') && (nlh || self.force_next_line) {
writeln!(self.writer)?;
}
Ok(())
}
}
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
pub(crate) fn write_all_args(&mut self) -> ClapResult<()> {
debugln!("Help::write_all_args;");
let flags = self.parser.has_flags();
let pos = positionals!(self.parser.app).fold(0, |acc, arg| {
if should_show_arg(self.use_long, arg) {
acc + 1
} else {
acc
}
}) > 0;
let opts = self.parser.has_opts();
let subcmds = self.parser.has_visible_subcommands();
let custom_headings = self.parser.app.args.args.iter().fold(0, |acc, arg| {
if arg.help_heading.is_some() {
acc + 1
} else {
acc
}
}) > 0;
let mut first = true;
if pos {
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning("ARGS:\n"))?;
self.write_args_unsorted(&*positionals!(self.parser.app).collect::<Vec<_>>())?;
first = false;
}
let unified_help = self.parser.is_set(AppSettings::UnifiedHelpMessage);
if unified_help && (flags || opts) {
let opts_flags = self
.parser
.app
.args
.args
.iter()
.filter(|a| a.has_switch())
.collect::<Vec<_>>();
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning("OPTIONS:\n"))?;
self.write_args(&*opts_flags)?;
first = false;
} else {
if flags {
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning("FLAGS:\n"))?;
let flags_v: Vec<_> = flags!(self.parser.app).collect();
self.write_args(&*flags_v)?;
first = false;
}
if opts {
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning("OPTIONS:\n"))?;
self.write_args(&*opts!(self.parser.app).collect::<Vec<_>>())?;
first = false;
}
if custom_headings {
for heading in self
.parser
.app
.help_headings
.iter()
.filter(|heading| heading.is_some())
.map(|heading| heading.unwrap())
{
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning(&*format!("{}:\n", heading)))?;
let args = self
.parser
.app
.args
.args
.iter()
.filter(|a| a.help_heading.is_some() && a.help_heading.unwrap() == heading)
.collect::<Vec<_>>();
self.write_args(&*args)?;
first = false
}
}
}
if subcmds {
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning("SUBCOMMANDS:\n"))?;
self.write_subcommands(&self.parser.app)?;
}
Ok(())
}
fn write_subcommands(&mut self, app: &App<'b>) -> io::Result<()> {
debugln!("Help::write_subcommands;");
self.longest = 2;
let mut ord_m = VecMap::new();
for sc in subcommands!(app).filter(|s| !s.is_set(AppSettings::Hidden)) {
let btm = ord_m.entry(sc.disp_ord).or_insert(BTreeMap::new());
self.longest = cmp::max(self.longest, str_width(sc.name.as_str()));
btm.insert(sc.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_subcommand(sc)?;
}
}
Ok(())
}
fn write_version(&mut self) -> io::Result<()> {
debugln!("Help::write_version;");
write!(self.writer, "{}", self.parser.app.version.unwrap_or(""))?;
Ok(())
}
fn write_bin_name(&mut self) -> io::Result<()> {
debugln!("Help::write_bin_name;");
let term_w = self.term_w;
macro_rules! write_name {
() => {{
self.color(Format::Good(&*wrap_help(
&self.parser.app.name.replace("{n}", "\n"),
term_w,
)))?;
}};
}
if let Some(bn) = self.parser.app.bin_name.as_ref() {
if bn.contains(' ') {
self.color(Format::Good(&*bn.replace(" ", "-")))?
} else {
write_name!();
}
} else {
write_name!();
}
Ok(())
}
pub(crate) fn write_default_help(&mut self) -> ClapResult<()> {
debugln!("Help::write_default_help;");
if let Some(h) = self.parser.app.pre_help {
self.write_before_after_help(h)?;
self.writer.write_all(b"\n\n")?;
}
macro_rules! write_thing {
($thing:expr) => {{
write!(
self.writer,
"{}\n",
wrap_help(&$thing.replace("{n}", "\n"), self.term_w)
)?
}};
}
self.write_bin_name()?;
self.writer.write_all(b" ")?;
self.write_version()?;
self.writer.write_all(b"\n")?;
if let Some(author) = self.parser.app.author {
write_thing!(author)
}
if self.use_long && self.parser.app.long_about.is_some() {
debugln!("Help::write_default_help: writing long about");
write_thing!(self.parser.app.long_about.unwrap())
} else if self.parser.app.about.is_some() {
debugln!("Help::write_default_help: writing about");
write_thing!(self.parser.app.about.unwrap())
}
self.color(Format::Warning("\nUSAGE:"))?;
write!(
self.writer,
"\n{}{}\n\n",
TAB,
Usage::new(self.parser).create_usage_no_title(&[])
)?;
let flags = self.parser.has_flags();
let pos = self.parser.has_positionals();
let opts = self.parser.has_opts();
let subcmds = self.parser.has_subcommands();
if flags || opts || pos || subcmds {
self.write_all_args()?;
}
if let Some(h) = self.parser.app.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)
}
}
enum CopyUntilResult {
DelimiterFound(usize),
DelimiterNotFound(usize),
ReaderEmpty,
ReadError(io::Error),
WriteError(io::Error),
}
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
}
}
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;");
match copy_until(r, w, b'{') {
ReaderEmpty | DelimiterNotFound(_) => None,
ReadError(e) | WriteError(e) => Some(Err(e)),
DelimiterFound(_) => {
tag_buffer.set_position(0);
let buffer_size = tag_buffer.get_ref().len();
let mut rb = r.take(buffer_size as u64);
match copy_until(&mut rb, tag_buffer, b'}') {
ReaderEmpty => None,
DelimiterFound(tag_length) => Some(Ok(tag_length)),
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)),
}
}
}
}
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
fn write_templated_help(&mut self, template: &str) -> ClapResult<()> {
debugln!("Help::write_templated_help;");
let mut tmplr = Cursor::new(&template);
let mut tag_buf = Cursor::new(vec![0u8; 15]);
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={};",
String::from_utf8_lossy(&tag_buf.get_ref()[0..tag_length])
);
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()?;
}
b"version" => {
write!(
self.writer,
"{}",
self.parser.app.version.unwrap_or("unknown version")
)?;
}
b"author" => {
write!(
self.writer,
"{}",
self.parser.app.author.unwrap_or("unknown author")
)?;
}
b"about" => {
write!(
self.writer,
"{}",
self.parser.app.about.unwrap_or("unknown about")
)?;
}
b"long-about" => {
write!(
self.writer,
"{}",
self.parser.app.long_about.unwrap_or("unknown about")
)?;
}
b"usage" => {
write!(
self.writer,
"{}",
Usage::new(self.parser).create_usage_no_title(&[])
)?;
}
b"all-args" => {
self.write_all_args()?;
}
b"unified" => {
let opts_flags = self
.parser
.app
.args
.args
.iter()
.filter(|a| a.has_switch())
.collect::<Vec<_>>();
self.write_args(&*opts_flags)?;
}
b"flags" => {
self.write_args(&*flags!(self.parser.app).collect::<Vec<_>>())?;
}
b"options" => {
self.write_args(&*opts!(self.parser.app).collect::<Vec<_>>())?;
}
b"positionals" => {
self.write_args(&*positionals!(self.parser.app).collect::<Vec<_>>())?;
}
b"subcommands" => {
self.write_subcommands(self.parser.app)?;
}
b"after-help" => {
write!(
self.writer,
"{}",
self.parser.app.more_help.unwrap_or("unknown after-help")
)?;
}
b"before-help" => {
write!(
self.writer,
"{}",
self.parser.app.pre_help.unwrap_or("unknown before-help")
)?;
}
r => {
self.writer.write_all(b"{")?;
self.writer.write_all(r)?;
self.writer.write_all(b"}")?;
}
}
}
}
}
fn should_show_arg(use_long: bool, arg: &Arg) -> bool {
debugln!(
"Help::should_show_arg: use_long={:?}, arg={}",
use_long,
arg.name
);
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)
}
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");
}
}