1824: Finished color refactor r=CreepySkeleton a=pksunkara



Co-authored-by: Pavan Kumar Sunkara <pavan.sss1991@gmail.com>
This commit is contained in:
bors[bot] 2020-04-16 12:35:50 +00:00 committed by GitHub
commit 4c3a7f7998
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 861 additions and 941 deletions

View file

@ -69,7 +69,7 @@ indexmap = "1.0.1"
strsim = { version = "0.9.0", optional = true }
yaml-rust = { version = "0.4.1", optional = true }
atty = { version = "0.2", optional = true }
ansi_term = { version = "0.12", optional = true }
termcolor = { version = "1.1", optional = true }
vec_map = { version = "0.8", optional = true }
term_size = { version = "1.0.0-beta1", optional = true }
lazy_static = { version = "1", optional = true }
@ -85,7 +85,7 @@ criterion = { git = "git://github.com/pksunkara/criterion.rs", version = "0.3" }
default = ["suggestions", "color", "vec_map", "derive", "std", "cargo"]
std = [] # support for no_std in a backwards-compatible way
suggestions = ["strsim"]
color = ["ansi_term", "atty"]
color = ["atty", "termcolor"]
wrap_help = ["term_size", "textwrap/term_size"]
derive = ["clap_derive", "lazy_static"]
yaml = ["yaml-rust"]

View file

@ -81,7 +81,10 @@ fn test_parse_hex() {
);
let err = HexOpt::try_parse_from(&["test", "-n", "gg"]).unwrap_err();
assert!(err.message.contains("invalid digit found in string"), err);
assert!(
err.to_string().contains("invalid digit found in string"),
err
);
}
fn custom_parser_1(_: &str) -> &'static str {

View file

@ -147,5 +147,8 @@ fn test_parse_hex_function_path() {
);
let err = HexOpt::try_parse_from(&["test", "-n", "gg"]).unwrap_err();
assert!(err.message.contains("invalid digit found in string"), err);
assert!(
err.to_string().contains("invalid digit found in string"),
err
);
}

View file

@ -5,7 +5,7 @@
// Accept and endure. Do not touch.
#![allow(unused)]
use clap::IntoApp;
use clap::{find_subcmd_mut, match_alias, IntoApp};
pub fn get_help<T: IntoApp>() -> String {
let mut output = Vec::new();
@ -32,10 +32,12 @@ pub fn get_long_help<T: IntoApp>() -> String {
}
pub fn get_subcommand_long_help<T: IntoApp>(subcmd: &str) -> String {
let output = <T as IntoApp>::into_app()
.try_get_matches_from(vec!["test", subcmd, "--help"])
.expect_err("")
.message;
let mut output = Vec::new();
find_subcmd_mut!(<T as IntoApp>::into_app(), subcmd)
.unwrap()
.write_long_help(&mut output)
.unwrap();
let output = String::from_utf8(output).unwrap();
eprintln!(
"\n%%% SUBCOMMAND `{}` HELP %%%:=====\n{}\n=====\n",

View file

@ -178,7 +178,7 @@ fn option_details_for_path(app: &App, path: &str) -> String {
}
fn vals_for(o: &Arg) -> String {
debugln!("Bash::vals_for: o={}", o.name);
debugln!("Bash::vals_for: o={}", o.get_name());
if let Some(ref vals) = o.get_possible_values() {
format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "))

View file

@ -9,7 +9,7 @@ use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::io::{self, BufRead, BufWriter, Write};
use std::io::{self, BufRead, Write};
use std::path::Path;
use std::process;
@ -20,11 +20,10 @@ use yaml_rust::Yaml;
// Internal
use crate::build::{app::settings::AppFlags, Arg, ArgGroup, ArgSettings};
use crate::mkeymap::MKeyMap;
use crate::output::fmt::ColorWhen;
use crate::output::{Help, Usage};
use crate::output::{fmt::Colorizer, Help, HelpWriter, Usage};
use crate::parse::errors::Result as ClapResult;
use crate::parse::{ArgMatcher, ArgMatches, Input, Parser};
use crate::util::{Id, Key};
use crate::util::{termcolor::ColorChoice, Id, Key};
use crate::INTERNAL_ERROR_MSG;
// FIXME (@CreepySkeleton): some of this variants are never constructed
@ -1087,13 +1086,14 @@ impl<'b> App<'b> {
/// [`-h` (short)]: ./struct.Arg.html#method.help
/// [`--help` (long)]: ./struct.Arg.html#method.long_help
pub fn print_help(&mut self) -> ClapResult<()> {
// If there are global arguments, or settings we need to propagate them down to subcommands
// before parsing incase we run into a subcommand
self._build();
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
self.write_help(&mut buf_w)
let p = Parser::new(self);
let mut c = Colorizer::new(false, p.color_help());
Help::new(HelpWriter::Buffer(&mut c), &p, true).write_help()?;
Ok(c.print()?)
}
/// Prints the full help message to [`io::stdout()`] using a [`BufWriter`] using the same
@ -1114,13 +1114,14 @@ impl<'b> App<'b> {
/// [`-h` (short)]: ./struct.Arg.html#method.help
/// [`--help` (long)]: ./struct.Arg.html#method.long_help
pub fn print_long_help(&mut self) -> ClapResult<()> {
// If there are global arguments, or settings we need to propagate them down to subcommands
// before parsing incase we run into a subcommand
self._build();
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
self.write_long_help(&mut buf_w)
let p = Parser::new(self);
let mut c = Colorizer::new(false, p.color_help());
Help::new(HelpWriter::Buffer(&mut c), &p, true).write_help()?;
Ok(c.print()?)
}
/// Writes the full help message to the user to a [`io::Write`] object in the same method as if
@ -1145,7 +1146,7 @@ impl<'b> App<'b> {
self._build();
let p = Parser::new(self);
Help::new(w, &p, false, false).write_help()
Help::new(HelpWriter::Normal(w), &p, false).write_help()
}
/// Writes the full help message to the user to a [`io::Write`] object in the same method as if
@ -1170,7 +1171,7 @@ impl<'b> App<'b> {
self._build();
let p = Parser::new(self);
Help::new(w, &p, true, false).write_help()
Help::new(HelpWriter::Normal(w), &p, true).write_help()
}
/// Writes the version message to the user to a [`io::Write`] object as if the user ran `-V`.
@ -1264,13 +1265,15 @@ impl<'b> App<'b> {
.unwrap_or_else(|e| {
// Otherwise, write to stderr and exit
if e.use_stderr() {
wlnerr!("{}", e.message);
e.message.print().expect("Error writing Error to stderr");
if self.settings.is_set(AppSettings::WaitOnError) {
wlnerr!("\nPress [ENTER] / [RETURN] to continue...");
let mut s = String::new();
let i = io::stdin();
i.lock().read_line(&mut s).unwrap();
}
drop(e);
process::exit(2);
}
@ -1294,7 +1297,7 @@ impl<'b> App<'b> {
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .try_get_matches()
/// .unwrap_or_else( |e| e.exit() );
/// .unwrap_or_else(|e| e.exit());
/// ```
/// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html
/// [`ErrorKind::HelpDisplayed`]: ./enum.ErrorKind.html#variant.HelpDisplayed
@ -1338,13 +1341,15 @@ impl<'b> App<'b> {
self.try_get_matches_from_mut(itr).unwrap_or_else(|e| {
// Otherwise, write to stderr and exit
if e.use_stderr() {
wlnerr!("{}", e.message);
e.message.print().expect("Error writing Error to stderr");
if self.settings.is_set(AppSettings::WaitOnError) {
wlnerr!("\nPress [ENTER] / [RETURN] to continue...");
let mut s = String::new();
let i = io::stdin();
i.lock().read_line(&mut s).unwrap();
}
drop(self);
drop(e);
process::exit(2);
@ -1375,7 +1380,7 @@ impl<'b> App<'b> {
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .try_get_matches_from(arg_vec)
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// .unwrap_or_else(|e| e.exit());
/// ```
/// [`App::get_matches_from`]: ./struct.App.html#method.get_matches_from
/// [`App::try_get_matches`]: ./struct.App.html#method.try_get_matches
@ -1411,7 +1416,7 @@ impl<'b> App<'b> {
/// let mut app = App::new("myprog");
/// // Args and options go here...
/// let matches = app.try_get_matches_from_mut(arg_vec)
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// .unwrap_or_else(|e| e.exit());
/// ```
/// [`App`]: ./struct.App.html
/// [`App::try_get_matches_from`]: ./struct.App.html#method.try_get_matches_from
@ -1481,9 +1486,6 @@ impl<'b> App<'b> {
pub fn _build(&mut self) {
debugln!("App::_build;");
#[cfg(all(feature = "color", windows))]
let _ = ansi_term::enable_ansi_support();
// Make sure all the globally set flags apply to us as well
self.settings = self.settings | self.g_settings;
@ -1896,19 +1898,20 @@ impl<'b> App<'b> {
self.args.args.iter().find(|a| a.id == *arg_id)
}
// Should we color the output? None=determined by output location, true=yes, false=no
pub(crate) fn color(&self) -> ColorWhen {
// Should we color the output?
pub(crate) fn color(&self) -> ColorChoice {
debugln!("App::color;");
debug!("App::color: Color setting...");
if self.is_set(AppSettings::ColorNever) {
sdebugln!("Never");
ColorWhen::Never
ColorChoice::Never
} else if self.is_set(AppSettings::ColorAlways) {
sdebugln!("Always");
ColorWhen::Always
ColorChoice::Always
} else {
sdebugln!("Auto");
ColorWhen::Auto
ColorChoice::Auto
}
}

View file

@ -768,19 +768,6 @@ mod debug_macros {
}
}
// Helper/deduplication macro for printing the correct number of spaces in help messages
// used in:
// src/args/arg_builder/*.rs
// src/app/mod.rs
macro_rules! write_nspaces {
($dst:expr, $num:expr) => {{
debugln!("write_spaces!: num={}", $num);
for _ in 0..$num {
$dst.write_all(b" ")?;
}
}};
}
#[macro_export]
#[doc(hidden)]
macro_rules! flags {
@ -853,6 +840,7 @@ macro_rules! find_subcmd {
macro_rules! find_subcmd_mut {
($app:expr, $sc:expr) => {{
$app.get_subcommands_mut()
.iter_mut()
.find(|a| match_alias!(a, $sc, a.get_name()))
}};
}

View file

@ -1,187 +1,120 @@
use crate::util::termcolor::{Buffer, BufferWriter, ColorChoice};
#[cfg(feature = "color")]
use ansi_term::{
ANSIString,
Colour::{Green, Red, Yellow},
};
use crate::util::termcolor::{Color, ColorSpec, WriteColor};
use std::env;
use std::fmt;
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ColorWhen {
Auto,
Always,
Never,
}
use std::fmt::{self, Debug, Formatter};
use std::io::{Result, Write};
#[cfg(feature = "color")]
pub(crate) fn is_a_tty(stderr: bool) -> bool {
fn is_a_tty(stderr: bool) -> bool {
debugln!("is_a_tty: stderr={:?}", stderr);
let stream = if stderr {
atty::Stream::Stderr
} else {
atty::Stream::Stdout
};
atty::is(stream)
}
#[cfg(not(feature = "color"))]
pub(crate) fn is_a_tty(_: bool) -> bool {
fn is_a_tty(_: bool) -> bool {
debugln!("is_a_tty;");
false
}
pub(crate) fn is_term_dumb() -> bool {
env::var("TERM").ok() == Some(String::from("dumb"))
}
pub(crate) struct ColorizerOption {
pub(crate) use_stderr: bool,
pub(crate) when: ColorWhen,
}
pub(crate) struct Colorizer {
when: ColorWhen,
writer: BufferWriter,
buffer: Buffer,
}
macro_rules! color {
($_self:ident, $c:ident, $m:expr) => {
match $_self.when {
ColorWhen::Auto => Format::$c($m),
ColorWhen::Always => Format::$c($m),
ColorWhen::Never => Format::None($m),
}
};
impl Debug for Colorizer {
#[cfg(feature = "color")]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(self.buffer.as_slice()))
}
#[cfg(not(feature = "color"))]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.buffer))
}
}
impl Colorizer {
pub(crate) fn new(option: &ColorizerOption) -> Colorizer {
let is_a_tty = is_a_tty(option.use_stderr);
let is_term_dumb = is_term_dumb();
Colorizer {
when: if is_a_tty && !is_term_dumb {
option.when
} else {
ColorWhen::Never
},
}
pub(crate) fn new(use_stderr: bool, when: ColorChoice) -> Self {
let checked_when = if is_a_tty(use_stderr) {
when
} else {
ColorChoice::Never
};
let writer = if use_stderr {
BufferWriter::stderr(checked_when)
} else {
BufferWriter::stdout(checked_when)
};
let buffer = writer.buffer();
Self { writer, buffer }
}
pub(crate) fn good<T>(&self, msg: T) -> Format<T>
where
T: fmt::Display + AsRef<str>,
{
debugln!("Colorizer::good;");
color!(self, Good, msg)
pub(crate) fn print(&self) -> Result<()> {
self.writer.print(&self.buffer)
}
pub(crate) fn warning<T>(&self, msg: T) -> Format<T>
where
T: fmt::Display + AsRef<str>,
{
debugln!("Colorizer::warning;");
color!(self, Warning, msg)
#[cfg(feature = "color")]
pub(crate) fn good(&mut self, msg: &str) -> Result<()> {
self.buffer
.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
self.write_all(msg.as_bytes())?;
self.buffer.reset()
}
pub(crate) fn error<T>(&self, msg: T) -> Format<T>
where
T: fmt::Display + AsRef<str>,
{
debugln!("Colorizer::error;");
color!(self, Error, msg)
#[cfg(not(feature = "color"))]
pub(crate) fn good(&mut self, msg: &str) -> Result<()> {
self.none(msg)
}
pub(crate) fn none<T>(&self, msg: T) -> Format<T>
where
T: fmt::Display + AsRef<str>,
{
debugln!("Colorizer::none;");
Format::None(msg)
#[cfg(feature = "color")]
pub(crate) fn warning(&mut self, msg: &str) -> Result<()> {
self.buffer
.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
self.write_all(msg.as_bytes())?;
self.buffer.reset()
}
#[cfg(not(feature = "color"))]
pub(crate) fn warning(&mut self, msg: &str) -> Result<()> {
self.none(msg)
}
#[cfg(feature = "color")]
pub(crate) fn error(&mut self, msg: &str) -> Result<()> {
self.buffer
.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
self.write_all(msg.as_bytes())?;
self.buffer.reset()
}
#[cfg(not(feature = "color"))]
pub(crate) fn error(&mut self, msg: &str) -> Result<()> {
self.none(msg)
}
pub(crate) fn none(&mut self, msg: &str) -> Result<()> {
self.write_all(msg.as_bytes())?;
Ok(())
}
}
impl Default for Colorizer {
fn default() -> Self {
Colorizer::new(&ColorizerOption {
use_stderr: true,
when: ColorWhen::Auto,
})
}
}
/// Defines styles for different types of error messages. Defaults to Error=Red, Warning=Yellow,
/// and Good=Green
#[derive(Debug)]
pub(crate) enum Format<T> {
/// Defines the style used for errors, defaults to Red
Error(T),
/// Defines the style used for warnings, defaults to Yellow
Warning(T),
/// Defines the style used for good values, defaults to Green
Good(T),
/// Defines no formatting style
None(T),
}
#[cfg(feature = "color")]
impl<T: AsRef<str>> Format<T> {
fn format(&self) -> ANSIString {
match *self {
Format::Error(ref e) => Red.bold().paint(e.as_ref()),
Format::Warning(ref e) => Yellow.paint(e.as_ref()),
Format::Good(ref e) => Green.paint(e.as_ref()),
Format::None(ref e) => ANSIString::from(e.as_ref()),
}
}
}
#[cfg(not(feature = "color"))]
impl<T: fmt::Display> Format<T> {
fn format(&self) -> &T {
match *self {
Format::Error(ref e) => e,
Format::Warning(ref e) => e,
Format::Good(ref e) => e,
Format::None(ref e) => e,
}
}
}
#[cfg(feature = "color")]
impl<T: AsRef<str>> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}
#[cfg(not(feature = "color"))]
impl<T: fmt::Display> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}
#[cfg(all(test, feature = "color"))]
mod test {
use super::Format;
use ansi_term::ANSIString;
use ansi_term::Colour::{Green, Red, Yellow};
#[test]
fn colored_output() {
let err = Format::Error("error");
assert_eq!(
&*format!("{}", err),
&*format!("{}", Red.bold().paint("error"))
);
let good = Format::Good("good");
assert_eq!(&*format!("{}", good), &*format!("{}", Green.paint("good")));
let warn = Format::Warning("warn");
assert_eq!(&*format!("{}", warn), &*format!("{}", Yellow.paint("warn")));
let none = Format::None("none");
assert_eq!(
&*format!("{}", none),
&*format!("{}", ANSIString::from("none"))
);
impl Write for Colorizer {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.buffer.write(buf)
}
fn flush(&mut self) -> Result<()> {
self.buffer.flush()
}
}

View file

@ -7,8 +7,7 @@ use std::usize;
// Internal
use crate::build::{App, AppSettings, Arg, ArgSettings};
use crate::output::fmt::{Colorizer, ColorizerOption, Format};
use crate::output::Usage;
use crate::output::{fmt::Colorizer, Usage};
use crate::parse::errors::{Error, Result as ClapResult};
use crate::parse::Parser;
use crate::util::VecMap;
@ -29,17 +28,36 @@ fn str_width(s: &str) -> usize {
const TAB: &str = " ";
pub(crate) enum HelpWriter<'w> {
Normal(&'w mut dyn Write),
Buffer(&'w mut Colorizer),
}
impl<'w> Write for HelpWriter<'w> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
HelpWriter::Normal(n) => n.write(buf),
HelpWriter::Buffer(c) => c.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
HelpWriter::Normal(n) => n.flush(),
HelpWriter::Buffer(c) => c.flush(),
}
}
}
/// `clap` Help Writer.
///
/// Wraps a writer stream providing different methods to generate help for `clap` objects.
pub(crate) struct Help<'b, 'c, 'd, 'w> {
writer: &'w mut dyn Write,
writer: HelpWriter<'w>,
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,
@ -48,12 +66,7 @@ pub(crate) struct Help<'b, 'c, 'd, 'w> {
// Public Functions
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
/// Create a new `Help` instance.
pub(crate) fn new(
w: &'w mut dyn Write,
parser: &'d Parser<'b, 'c>,
use_long: bool,
stderr: bool,
) -> Self {
pub(crate) fn new(w: HelpWriter<'w>, parser: &'d Parser<'b, 'c>, use_long: bool) -> Self {
debugln!("Help::new;");
let term_w = match parser.app.term_w {
Some(0) => usize::MAX,
@ -68,21 +81,15 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
};
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,
}
}
@ -90,47 +97,56 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
/// Writes the parser help to the wrapped stream.
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)?;
self.none(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)?;
self.none("\n")?;
Ok(())
}
}
macro_rules! write_method {
($_self:ident, $msg:ident, $meth:ident) => {
match &mut $_self.writer {
HelpWriter::Buffer(c) => c.$meth($msg),
HelpWriter::Normal(w) => write!(w, "{}", $msg),
}
};
}
macro_rules! write_nspaces {
($_self:ident, $num:expr) => {{
debugln!("write_nspaces!: num={}", $num);
for _ in 0..$num {
$_self.none(" ")?;
}
}};
}
// Methods to write Arg help.
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 good(&mut self, msg: &str) -> io::Result<()> {
write_method!(self, msg, good)
}
fn warning(&mut self, msg: &str) -> io::Result<()> {
write_method!(self, msg, warning)
}
#[allow(dead_code)]
fn error(&mut self, msg: &str) -> io::Result<()> {
write_method!(self, msg, error)
}
fn none(&mut self, msg: &str) -> io::Result<()> {
write_method!(self, msg, none)
}
/// Writes help for each argument in the order they were declared to the wrapped stream.
@ -152,7 +168,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
if first {
first = false;
} else {
self.writer.write_all(b"\n")?;
self.none("\n")?;
}
self.write_arg(arg, i < arg_c)?;
}
@ -189,7 +205,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
if first {
first = false;
} else {
self.writer.write_all(b"\n")?;
self.none("\n")?;
}
self.write_arg(arg, false)?;
}
@ -210,11 +226,13 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
/// Writes argument's short command to the wrapped stream.
fn short(&mut self, arg: &Arg<'c>) -> io::Result<()> {
debugln!("Help::short;");
write!(self.writer, "{}", TAB)?;
self.none(TAB)?;
if let Some(s) = arg.short {
self.color(Format::Good(&*format!("-{}", s)))
self.good(&format!("-{}", s))
} else if arg.has_switch() {
write!(self.writer, "{}", TAB)
self.none(TAB)
} else {
Ok(())
}
@ -229,9 +247,9 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
if arg.is_set(ArgSettings::TakesValue) {
if let Some(l) = arg.long {
if arg.short.is_some() {
write!(self.writer, ", ")?;
self.none(", ")?;
}
self.color(Format::Good(&*format!("--{}", l)))?
self.good(&format!("--{}", l))?
}
let sep = if arg.is_set(ArgSettings::RequireEquals) {
@ -239,12 +257,12 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
} else {
" "
};
write!(self.writer, "{}", sep)?;
self.none(sep)?;
} else if let Some(l) = arg.long {
if arg.short.is_some() {
write!(self.writer, ", ")?;
self.none(", ")?;
}
self.color(Format::Good(&*format!("--{}", l)))?;
self.good(&format!("--{}", l))?;
}
Ok(())
}
@ -263,33 +281,33 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
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)))?;
self.good(&format!("<{}>", val))?;
if it.peek().is_some() {
write!(self.writer, "{}", delim)?;
self.none(&delim.to_string())?;
}
}
let num = vec.len();
if mult && num == 1 {
self.color(Format::Good("..."))?;
self.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)))?;
self.good(&format!("<{}>", arg.name))?;
if it.peek().is_some() {
write!(self.writer, "{}", delim)?;
self.none(&delim.to_string())?;
}
}
if mult && num == 1 {
self.color(Format::Good("..."))?;
self.good("...")?;
}
} else if arg.has_switch() {
self.color(Format::Good(&*format!("<{}>", arg.name)))?;
self.good(&format!("<{}>", arg.name))?;
if mult {
self.color(Format::Good("..."))?;
self.good("...")?;
}
} else {
self.color(Format::Good(&*arg.to_string()))?;
self.good(&arg.to_string())?;
}
}
@ -332,14 +350,14 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
spcs += 8;
}
write_nspaces!(self.writer, spcs);
write_nspaces!(self, spcs);
} else {
sdebugln!("Yes");
}
} else if !(nlh || self.force_next_line) {
sdebugln!("No, and not next_line");
write_nspaces!(
self.writer,
self,
self.longest + 4 - (str_width(arg.to_string().as_str()))
);
} else {
@ -375,7 +393,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
} else {
sdebugln!("No");
}
write!(self.writer, "{}", help)?;
self.none(&help)?;
Ok(())
}
@ -401,7 +419,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
// Is help on next line, if so then indent
if nlh || self.force_next_line {
write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?;
self.none(&format!("\n{}{}{}", TAB, TAB, TAB))?;
}
debug!("Help::help: Too long...");
@ -417,21 +435,21 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
sdebugln!("No");
}
if let Some(part) = help.lines().next() {
write!(self.writer, "{}", part)?;
self.none(part)?;
}
for part in help.lines().skip(1) {
writeln!(self.writer)?;
self.none("\n")?;
if nlh || self.force_next_line {
write!(self.writer, "{}{}{}", TAB, TAB, TAB)?;
self.none(&format!("{}{}{}", TAB, TAB, TAB))?;
} else if arg.has_switch() {
write_nspaces!(self.writer, self.longest + 12);
write_nspaces!(self, self.longest + 12);
} else {
write_nspaces!(self.writer, self.longest + 8);
write_nspaces!(self, self.longest + 8);
}
write!(self.writer, "{}", part)?;
self.none(part)?;
}
if !prevent_nlh && !help.contains('\n') && (nlh || self.force_next_line) {
writeln!(self.writer)?;
self.none("\n")?;
}
Ok(())
}
@ -462,37 +480,25 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
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(" ")
};
let pvs = pv
.iter()
.map(|&pvs| 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) // visible
.map(|&als| format!("{}", self.cizer.good(als.0))) // name
.collect::<Vec<_>>()
.join(", ")
} else {
aliases
.iter()
.filter(|&als| als.1)
.map(|&als| als.0)
.collect::<Vec<_>>()
.join(", ")
};
let als = aliases
.iter()
.filter(|&als| als.1) // visible
.map(|&als| als.0) // name
.collect::<Vec<_>>()
.join(", ");
if !als.is_empty() {
spec_vals.push(format!(" [aliases: {}]", als));
}
@ -500,17 +506,8 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
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.push(format!(" [possible values: {}]", pv.join(", ")));
}
}
spec_vals.join(" ")
@ -521,8 +518,8 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
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))?;
self.none(TAB)?;
self.good(&app.name)?;
let spec_vals = self.sc_val(app)?;
self.sc_help(app, &*spec_vals)?;
Ok(())
@ -541,10 +538,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
&& 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()))
);
write_nspaces!(self, self.longest + 4 - (str_width(&app.name)));
}
Ok(spec_vals)
}
@ -554,21 +548,14 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
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) // visible
.map(|&als| format!("{}", self.cizer.good(als.0))) // name
.collect::<Vec<_>>()
.join(", ")
} else {
aliases
.iter()
.filter(|&als| als.1)
.map(|&als| als.0)
.collect::<Vec<_>>()
.join(", ")
};
let als = aliases
.iter()
.filter(|&als| als.1) // visible
.map(|&als| als.0) // name
.collect::<Vec<_>>()
.join(", ");
if !als.is_empty() {
spec_vals.push(format!(" [aliases: {}]", als));
}
@ -597,7 +584,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
// Is help on next line, if so then indent
if nlh || self.force_next_line {
write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?;
self.none(&format!("\n{}{}{}", TAB, TAB, TAB))?;
}
debug!("Help::sc_help: Too long...");
@ -613,19 +600,19 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
sdebugln!("No");
}
if let Some(part) = help.lines().next() {
write!(self.writer, "{}", part)?;
self.none(part)?;
}
for part in help.lines().skip(1) {
writeln!(self.writer)?;
self.none("\n")?;
if nlh || self.force_next_line {
write!(self.writer, "{}{}{}", TAB, TAB, TAB)?;
self.none(&format!("{}{}{}", TAB, TAB, TAB))?;
} else {
write_nspaces!(self.writer, self.longest + 8);
write_nspaces!(self, self.longest + 8);
}
write!(self.writer, "{}", part)?;
self.none(part)?;
}
if !help.contains('\n') && (nlh || self.force_next_line) {
writeln!(self.writer)?;
self.none("\n")?;
}
Ok(())
}
@ -657,16 +644,13 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
}
}) > 0;
let mut first = true;
if pos {
if !first {
self.writer.write_all(b"\n\n")?;
}
self.color(Format::Warning("ARGS:\n"))?;
let mut first = if pos {
self.warning("ARGS:\n")?;
self.write_args_unsorted(&*positionals!(self.parser.app).collect::<Vec<_>>())?;
first = false;
}
false
} else {
true
};
let unified_help = self.parser.is_set(AppSettings::UnifiedHelpMessage);
@ -680,26 +664,26 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
.filter(|a| a.has_switch())
.collect::<Vec<_>>();
if !first {
self.writer.write_all(b"\n\n")?;
self.none("\n\n")?;
}
self.color(Format::Warning("OPTIONS:\n"))?;
self.warning("OPTIONS:\n")?;
self.write_args(&*opts_flags)?;
first = false;
} else {
if flags {
if !first {
self.writer.write_all(b"\n\n")?;
self.none("\n\n")?;
}
self.color(Format::Warning("FLAGS:\n"))?;
self.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.none("\n\n")?;
}
self.color(Format::Warning("OPTIONS:\n"))?;
self.warning("OPTIONS:\n")?;
self.write_args(&*opts!(self.parser.app).collect::<Vec<_>>())?;
first = false;
}
@ -713,9 +697,9 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
.map(|heading| heading.unwrap())
{
if !first {
self.writer.write_all(b"\n\n")?;
self.none("\n\n")?;
}
self.color(Format::Warning(&*format!("{}:\n", heading)))?;
self.warning(&*format!("{}:\n", heading))?;
let args = self
.parser
.app
@ -732,9 +716,9 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
if subcmds {
if !first {
self.writer.write_all(b"\n\n")?;
self.none("\n\n")?;
}
self.color(Format::Warning("SUBCOMMANDS:\n"))?;
self.warning("SUBCOMMANDS:\n")?;
self.write_subcommands(&self.parser.app)?;
}
@ -763,7 +747,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
if first {
first = false;
} else {
self.writer.write_all(b"\n")?;
self.none("\n")?;
}
self.write_subcommand(sc)?;
}
@ -774,7 +758,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
/// Writes version of a Parser Object to the wrapped stream.
fn write_version(&mut self) -> io::Result<()> {
debugln!("Help::write_version;");
write!(self.writer, "{}", self.parser.app.version.unwrap_or(""))?;
self.none(self.parser.app.version.unwrap_or(""))?;
Ok(())
}
@ -784,13 +768,13 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
let term_w = self.term_w;
macro_rules! write_name {
() => {{
self.color(Format::Good(&*wrap_help(&self.parser.app.name, term_w)))?;
self.good(&*wrap_help(&self.parser.app.name, term_w))?;
}};
}
if let Some(bn) = self.parser.app.bin_name.as_ref() {
if bn.contains(' ') {
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
self.color(Format::Good(&*bn.replace(" ", "-")))?
self.good(&bn.replace(" ", "-"))?
} else {
write_name!();
}
@ -805,38 +789,41 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
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")?;
self.none("\n\n")?;
}
macro_rules! write_thing {
($thing:expr) => {{
write!(self.writer, "{}\n", wrap_help(&$thing, self.term_w))?
self.none(&wrap_help(&$thing, self.term_w))?;
self.none("\n")?
}};
}
// Print the version
self.write_bin_name()?;
self.writer.write_all(b" ")?;
self.none(" ")?;
self.write_version()?;
self.writer.write_all(b"\n")?;
self.none("\n")?;
if let Some(author) = self.parser.app.author {
write_thing!(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())
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())
write_thing!(self.parser.app.about.unwrap());
}
self.color(Format::Warning("\nUSAGE:"))?;
write!(
self.writer,
self.none("\n")?;
self.warning("USAGE:")?;
self.none(&format!(
"\n{}{}\n\n",
TAB,
Usage::new(self.parser).create_usage_no_title(&[])
)?;
))?;
let flags = self.parser.has_flags();
let pos = self.parser.has_positionals();
@ -849,7 +836,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
if let Some(h) = self.parser.app.more_help {
if flags || opts || pos || subcmds {
self.writer.write_all(b"\n\n")?;
self.none("\n\n")?;
}
self.write_before_after_help(h)?;
}
@ -1002,45 +989,25 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
);
match &tag_buf.get_ref()[0..tag_length] {
b"?" => {
self.writer.write_all(b"Could not decode tag name")?;
self.none("Could not decode tag name")?;
}
b"bin" => {
self.write_bin_name()?;
}
b"version" => {
write!(
self.writer,
"{}",
self.parser.app.version.unwrap_or("unknown version")
)?;
self.none(self.parser.app.version.unwrap_or("unknown version"))?;
}
b"author" => {
write!(
self.writer,
"{}",
self.parser.app.author.unwrap_or("unknown author")
)?;
self.none(self.parser.app.author.unwrap_or("unknown author"))?;
}
b"about" => {
write!(
self.writer,
"{}",
self.parser.app.about.unwrap_or("unknown about")
)?;
self.none(self.parser.app.about.unwrap_or("unknown about"))?;
}
b"long-about" => {
write!(
self.writer,
"{}",
self.parser.app.long_about.unwrap_or("unknown about")
)?;
self.none(self.parser.app.long_about.unwrap_or("unknown about"))?;
}
b"usage" => {
write!(
self.writer,
"{}",
Usage::new(self.parser).create_usage_no_title(&[])
)?;
self.none(&Usage::new(self.parser).create_usage_no_title(&[]))?;
}
b"all-args" => {
self.write_all_args()?;
@ -1069,24 +1036,16 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
self.write_subcommands(self.parser.app)?;
}
b"after-help" => {
write!(
self.writer,
"{}",
self.parser.app.more_help.unwrap_or("unknown after-help")
)?;
self.none(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")
)?;
self.none(self.parser.app.pre_help.unwrap_or("unknown before-help"))?;
}
// Unknown tag, write it back.
r => {
self.writer.write_all(b"{")?;
self.none("{")?;
self.writer.write_all(r)?;
self.writer.write_all(b"}")?;
self.none("}")?;
}
}
}

View file

@ -1,7 +1,7 @@
mod help;
mod usage;
pub mod fmt;
pub(crate) mod fmt;
pub(crate) use self::help::Help;
pub(crate) use self::help::{Help, HelpWriter};
pub(crate) use self::usage::Usage;

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,6 @@ use std::cmp::Ordering;
// Internal
use crate::build::App;
use crate::output::fmt::Format;
/// Produces multiple strings from a given list of possible values which are similar
/// to the passed in value `v` within a certain confidence by least confidence.
@ -34,23 +33,18 @@ where
}
/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
pub(crate) fn did_you_mean_flag_suffix<I, T>(
pub(crate) fn did_you_mean_flag<I, T>(
arg: &str,
longs: I,
subcommands: &mut [App],
) -> (String, Option<String>)
) -> Option<(String, Option<String>)>
where
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
match did_you_mean(arg, longs).pop() {
Some(ref candidate) => {
let suffix = format!(
"\n\tDid you mean {}{}?",
Format::Good("--"),
Format::Good(candidate)
);
return (suffix, Some(candidate.to_owned()));
return Some((candidate.to_owned(), None));
}
None => {
for subcommand in subcommands {
@ -61,33 +55,13 @@ where
)
.pop()
{
let suffix = format!(
"\n\tDid you mean to put '{}{}' after the subcommand '{}'?",
Format::Good("--"),
Format::Good(candidate),
Format::Good(subcommand.get_name())
);
return (suffix, Some(candidate.clone()));
return Some((candidate.to_owned(), Some(subcommand.get_name().to_owned())));
}
}
}
}
(String::new(), None)
}
/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
pub(crate) fn did_you_mean_value_suffix<T, I>(arg: &str, values: I) -> (String, Option<String>)
where
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
match did_you_mean(arg, values).pop() {
Some(ref candidate) => {
let suffix = format!("\n\tDid you mean '{}'?", Format::Good(candidate));
(suffix, Some(candidate.to_owned()))
}
None => (String::new(), None),
}
None
}
#[cfg(all(test, features = "suggestions"))]
@ -113,22 +87,11 @@ mod test {
}
#[test]
fn suffix_long() {
fn flag() {
let p_vals = ["test", "possible", "values"];
let suffix = "\n\tDid you mean \'--test\'?";
assert_eq!(
did_you_mean_flag_suffix("tst", p_vals.iter(), []),
(suffix, Some("test"))
);
}
#[test]
fn suffix_enum() {
let p_vals = ["test", "possible", "values"];
let suffix = "\n\tDid you mean \'test\'?";
assert_eq!(
did_you_mean_value_suffix("tst", p_vals.iter()),
(suffix, Some("test"))
did_you_mean_flag("tst", p_vals.iter(), []),
Some(("test", None))
);
}
}

View file

@ -1,7 +1,7 @@
// Std
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use std::fmt::{Debug, Display};
use std::iter::{Cloned, Map};
use std::slice::Iter;
use std::str::FromStr;
@ -334,14 +334,14 @@ impl ArgMatches {
<R as FromStr>::Err: Display,
{
if let Some(v) = self.value_of(name) {
v.parse::<R>().map_err(|e| {
Error::value_validation_auto(&format!(
v.parse::<R>().or_else(|e| {
Err(Error::value_validation_auto(&format!(
"The argument '{}' isn't a valid value: {}",
v, e
))
))?)
})
} else {
Err(Error::argument_not_found_auto(name))
Err(Error::argument_not_found_auto(name)?)
}
}
@ -420,16 +420,16 @@ impl ArgMatches {
{
if let Some(vals) = self.values_of(name) {
vals.map(|v| {
v.parse::<R>().map_err(|e| {
Error::value_validation_auto(&format!(
v.parse::<R>().or_else(|e| {
Err(Error::value_validation_auto(&format!(
"The argument '{}' isn't a valid value: {}",
v, e
))
))?)
})
})
.collect()
} else {
Err(Error::argument_not_found_auto(name))
Err(Error::argument_not_found_auto(name)?)
}
}

View file

@ -11,7 +11,7 @@ use crate::build::app::Propagation;
use crate::build::AppSettings as AS;
use crate::build::{App, Arg, ArgSettings};
use crate::mkeymap::KeyType;
use crate::output::{Help, Usage};
use crate::output::{fmt::Colorizer, Help, HelpWriter, Usage};
use crate::parse::errors::Error as ClapError;
use crate::parse::errors::ErrorKind;
use crate::parse::errors::Result as ClapResult;
@ -20,7 +20,7 @@ use crate::parse::Validator;
use crate::parse::{ArgMatcher, SubCommand};
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
use crate::util::OsStrExt3;
use crate::util::{ChildGraph, Id, OsStrExt2};
use crate::util::{termcolor::ColorChoice, ChildGraph, Id, OsStrExt2};
use crate::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8;
@ -523,7 +523,7 @@ where
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
}
ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => {
@ -556,7 +556,7 @@ where
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
}
}
@ -655,7 +655,7 @@ where
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
if !self.is_set(AS::TrailingValues)
@ -696,7 +696,7 @@ where
return Err(ClapError::invalid_utf8(
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
arg_os.to_string_lossy().into_owned()
}
@ -710,7 +710,7 @@ where
return Err(ClapError::invalid_utf8(
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
sc_m.add_val_to(&Id::empty_hash(), &v);
}
@ -732,7 +732,7 @@ where
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
let cands =
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
@ -744,13 +744,13 @@ where
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
} else {
return Err(ClapError::unrecognized_subcommand(
arg_os.to_string_lossy().into_owned(),
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
self.app.color(),
));
)?);
}
} else {
return Err(ClapError::unknown_argument(
@ -758,7 +758,7 @@ where
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
}
@ -776,14 +776,13 @@ where
bn,
&Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
} else if self.is_set(AS::SubcommandRequiredElseHelp) {
debugln!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
let mut out = vec![];
self.write_help_err(&mut out)?;
let message = self.write_help_err()?;
return Err(ClapError {
cause: String::new(),
message: String::from_utf8_lossy(&*out).into_owned(),
message,
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
@ -794,6 +793,17 @@ where
Validator::new(self).validate(needs_val_of, subcmd_name.is_some(), matcher)
}
// Should we color the help?
pub(crate) fn color_help(&self) -> ColorChoice {
debugln!("App::color_help;");
if self.is_set(AS::ColoredHelp) {
self.app.color()
} else {
ColorChoice::Never
}
}
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
fn possible_subcommand(&self, arg_os: &OsStr) -> Option<&str> {
debugln!("Parser::possible_subcommand: arg={:?}", arg_os);
@ -871,7 +881,7 @@ where
cmd.to_string_lossy().into_owned(),
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
self.app.color(),
));
)?);
}
bin_name = format!("{} {}", bin_name, &*sc.name);
@ -1228,7 +1238,7 @@ where
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
}
Ok(ret)
@ -1259,7 +1269,7 @@ where
opt,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
}
sdebugln!("Found - {:?}, len: {}", v, v.len());
debugln!(
@ -1274,7 +1284,7 @@ where
opt,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
)?);
} else {
sdebugln!("None");
}
@ -1575,15 +1585,19 @@ where
.collect::<Vec<_>>();
debugln!("Parser::did_you_mean_error: longs={:?}", longs);
let suffix = suggestions::did_you_mean_flag_suffix(
let did_you_mean = suggestions::did_you_mean_flag(
arg,
longs.iter().map(|ref x| &x[..]),
self.app.subcommands.as_mut_slice(),
);
// Add the arg to the matches to build a proper usage string
if let Some(ref name) = suffix.1 {
if let Some(opt) = self.app.args.get(&KeyType::Long(OsString::from(name))) {
if let Some(ref name) = did_you_mean {
if let Some(opt) = self
.app
.args
.get(&KeyType::Long(OsString::from(name.0.clone())))
{
for g in groups_for_arg!(self.app, opt.id) {
matcher.inc_occurrence_of(&g);
}
@ -1603,18 +1617,12 @@ where
.cloned()
.collect();
let did_you_mean_msg = if suffix.0.is_empty() {
None
} else {
Some(suffix.0)
};
Err(ClapError::unknown_argument(
&*format!("--{}", arg),
did_you_mean_msg,
did_you_mean,
&*Usage::new(self).create_usage_with_title(&*used),
self.app.color(),
))
)?)
}
// Prints the version to the user and exits if quit=true
@ -1623,8 +1631,12 @@ where
w.flush().map_err(ClapError::from)
}
pub(crate) fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> {
Help::new(w, self, false, true).write_help()
pub(crate) fn write_help_err(&self) -> ClapResult<Colorizer> {
let mut c = Colorizer::new(true, self.color_help());
Help::new(HelpWriter::Buffer(&mut c), self, false).write_help()?;
Ok(c)
}
fn help_err(&self, mut use_long: bool) -> ClapError {
@ -1632,13 +1644,15 @@ where
"Parser::help_err: use_long={:?}",
use_long && self.use_long_help()
);
use_long = use_long && self.use_long_help();
let mut buf = vec![];
match Help::new(&mut buf, self, use_long, false).write_help() {
let mut c = Colorizer::new(false, self.color_help());
match Help::new(HelpWriter::Buffer(&mut c), self, use_long).write_help() {
Err(e) => e,
_ => ClapError {
cause: String::new(),
message: String::from_utf8(buf).unwrap_or_default(),
message: c,
kind: ErrorKind::HelpDisplayed,
info: None,
},
@ -1647,12 +1661,14 @@ where
fn version_err(&self, use_long: bool) -> ClapError {
debugln!("Parser::version_err: ");
let mut buf = vec![];
match self.print_version(&mut buf, use_long) {
let mut c = Colorizer::new(false, self.app.color());
match self.print_version(&mut c, use_long) {
Err(e) => e,
_ => ClapError {
cause: String::new(),
message: String::from_utf8(buf).unwrap_or_default(),
message: c,
kind: ErrorKind::VersionDisplayed,
info: None,
},

View file

@ -1,7 +1,6 @@
// Internal
use crate::build::app::AppSettings as AS;
use crate::build::{Arg, ArgSettings};
use crate::output::fmt::{Colorizer, ColorizerOption};
use crate::output::Usage;
use crate::parse::errors::Result as ClapResult;
use crate::parse::errors::{Error, ErrorKind};
@ -53,7 +52,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
o,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
}
@ -61,11 +60,10 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
&& matcher.subcommand_name().is_none()
&& self.p.is_set(AS::ArgRequiredElseHelp)
{
let mut out = vec![];
self.p.write_help_err(&mut out)?;
let message = self.p.write_help_err()?;
return Err(Error {
cause: String::new(),
message: String::from_utf8_lossy(&*out).into_owned(),
message,
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
@ -96,7 +94,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
return Err(Error::invalid_utf8(
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
if let Some(ref p_vals) = arg.possible_vals {
debugln!("Validator::validate_arg_values: possible_vals={:?}", p_vals);
@ -124,7 +122,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
arg,
&*Usage::new(self.p).create_usage_with_title(&*used),
self.p.app.color(),
));
)?);
}
}
if !arg.is_set(ArgSettings::AllowEmptyValues)
@ -136,13 +134,13 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
arg,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
if let Some(ref vtor) = arg.validator {
debug!("Validator::validate_arg_values: checking validator...");
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg), &e, self.p.app.color()));
return Err(Error::value_validation(Some(arg), &e, self.p.app.color())?);
} else {
sdebugln!("good");
}
@ -155,7 +153,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
Some(arg),
&(*e).to_string(),
self.p.app.color(),
));
)?);
} else {
sdebugln!("good");
}
@ -177,7 +175,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
Some(other_arg.to_string()),
&*usg,
self.p.app.color(),
));
)?);
}
}
}
@ -198,7 +196,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
c_with,
&*usg,
self.p.app.color(),
));
)?);
}
panic!(INTERNAL_ERROR_MSG);
@ -269,17 +267,12 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
if let Some(arg) = self.p.app.find(name) {
if arg.exclusive && args_count > 1 {
let c_with: Option<String> = None;
let err = Err(Error::argument_conflict(
return Err(Error::argument_conflict(
arg,
c_with,
Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
debugln!(
"Validator::validate_conflicts_with_everything; ERROR: {:?}",
err
);
return err;
)?);
}
}
}
@ -407,7 +400,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
a,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
Ok(())
}
@ -433,7 +426,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
},
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
}
if let Some(num) = a.max_vals {
@ -450,7 +443,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
a,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
}
let min_vals_zero = if let Some(num) = a.min_vals {
@ -463,7 +456,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
ma.vals.len(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
num == 0
} else {
@ -476,7 +469,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
a,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
)?);
}
Ok(())
}
@ -619,32 +612,24 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
// `incl`: an arg to include in the error even if not used
fn missing_required_error(&self, matcher: &ArgMatcher, incl: Option<&Id>) -> ClapResult<()> {
debugln!("Validator::missing_required_error; incl={:?}", incl);
let c = Colorizer::new(&ColorizerOption {
use_stderr: true,
when: self.p.app.color(),
});
debugln!(
"Validator::missing_required_error: reqs={:?}",
self.p.required
);
let usg = Usage::new(self.p);
let req_args = if let Some(x) = incl {
usg.get_required_usage_from(&[x.clone()], Some(matcher), true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
})
} else {
usg.get_required_usage_from(&[], None, true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
})
};
debugln!(
"Validator::missing_required_error: req_args={:#?}",
req_args
);
let used: Vec<Id> = matcher
.arg_names()
.filter(|&n| {
@ -657,10 +642,11 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
.cloned()
.chain(incl.cloned())
.collect();
Err(Error::missing_required_argument(
&*req_args,
req_args,
&*usg.create_usage_with_title(&*used),
self.p.app.color(),
))
)?)
}
}

View file

@ -11,3 +11,9 @@ pub(crate) use self::{graph::ChildGraph, id::Id, map::VecMap, osstringext::OsStr
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
pub(crate) use self::osstringext::OsStrExt3;
#[cfg(feature = "color")]
pub(crate) use termcolor;
#[cfg(not(feature = "color"))]
pub(crate) mod termcolor;

38
src/util/termcolor.rs Normal file
View file

@ -0,0 +1,38 @@
use std::io::{stderr, stdout, Result, Write};
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ColorChoice {
Auto,
Always,
Never,
}
pub(crate) type Buffer = Vec<u8>;
pub(crate) struct BufferWriter {
use_stderr: bool,
}
impl BufferWriter {
pub(crate) fn buffer(&self) -> Buffer {
vec![]
}
pub(crate) fn stderr(_: ColorChoice) -> Self {
Self { use_stderr: true }
}
pub(crate) fn stdout(_: ColorChoice) -> Self {
Self { use_stderr: false }
}
pub(crate) fn print(&self, buf: &Buffer) -> Result<()> {
if self.use_stderr {
stderr().lock().write(buf)?;
} else {
stdout().lock().write(buf)?;
}
Ok(())
}
}

View file

@ -20,7 +20,7 @@ fn app_from_crate() {
let err = res.unwrap_err();
assert_eq!(err.kind, ErrorKind::HelpDisplayed);
assert_eq!(
err.message,
err.to_string(),
EVERYTHING.replace("{{version}}", env!("CARGO_PKG_VERSION"))
);
}

View file

@ -31,7 +31,10 @@ fn crate_version() {
assert!(res.is_err());
let err = res.unwrap_err();
assert_eq!(err.kind, ErrorKind::VersionDisplayed);
assert_eq!(err.message, format!("prog {}", env!("CARGO_PKG_VERSION")));
assert_eq!(
err.to_string(),
format!("prog {}", env!("CARGO_PKG_VERSION"))
);
}
#[test]
@ -44,7 +47,7 @@ fn crate_description() {
assert!(res.is_err());
let err = res.unwrap_err();
assert_eq!(err.kind, ErrorKind::HelpDisplayed);
assert_eq!(err.message, DESCRIPTION_ONLY);
assert_eq!(err.to_string(), DESCRIPTION_ONLY);
}
#[test]
@ -57,7 +60,7 @@ fn crate_authors() {
assert!(res.is_err());
let err = res.unwrap_err();
assert_eq!(err.kind, ErrorKind::HelpDisplayed);
assert_eq!(err.message, AUTHORS_ONLY);
assert_eq!(err.to_string(), AUTHORS_ONLY);
}
#[test]
@ -67,5 +70,5 @@ fn crate_name() {
assert!(res.is_err());
let err = res.unwrap_err();
assert_eq!(err.kind, ErrorKind::VersionDisplayed);
assert_eq!(err.message, "clap ");
assert_eq!(err.to_string(), "clap ");
}

View file

@ -87,18 +87,18 @@ fn multiple_occurrences_of_before_env() {
);
let m = app.clone().try_get_matches_from(vec![""]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 0);
let m = app.clone().try_get_matches_from(vec!["", "-v"]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 1);
let m = app.clone().try_get_matches_from(vec!["", "-vv"]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 2);
let m = app.clone().try_get_matches_from(vec!["", "-vvv"]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 3);
}
@ -114,17 +114,17 @@ fn multiple_occurrences_of_after_env() {
);
let m = app.clone().try_get_matches_from(vec![""]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 0);
let m = app.clone().try_get_matches_from(vec!["", "-v"]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 1);
let m = app.clone().try_get_matches_from(vec!["", "-vv"]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 2);
let m = app.clone().try_get_matches_from(vec!["", "-vvv"]);
assert!(m.is_ok(), "{}", m.unwrap_err().message);
assert!(m.is_ok(), "{}", m.unwrap_err());
assert_eq!(m.unwrap().occurrences_of("verbose"), 3);
}

View file

@ -5,7 +5,9 @@ use clap::{App, Arg, ArgMatches, ArgSettings, ErrorKind};
#[cfg(feature = "suggestions")]
static DYM: &str =
"error: Found argument '--optio' which wasn't expected, or isn't valid in this context
\tDid you mean --option?
\tDid you mean '--option'?
If you tried to supply `--optio` as a PATTERN use `-- --optio`
USAGE:
@ -16,7 +18,9 @@ For more information try --help";
#[cfg(feature = "suggestions")]
static DYM_ISSUE_1073: &str =
"error: Found argument '--files-without-matches' which wasn't expected, or isn't valid in this context
\tDid you mean --files-without-match?
\tDid you mean '--files-without-match'?
If you tried to supply `--files-without-matches` as a PATTERN use `-- --files-without-matches`
USAGE:

View file

@ -17,7 +17,6 @@ For more information try --help";
static PV_ERROR: &'static str = "error: 'slo' isn't a valid value for '--Option <option3>'
\t[possible values: fast, slow]
USAGE:
clap-test --Option <option3>

View file

@ -58,6 +58,7 @@ SUBCOMMANDS:
#[cfg(feature = "suggestions")]
static DYM_SUBCMD: &str = "error: The subcommand 'subcm' wasn't recognized
Did you mean 'subcmd'?
If you believe you received this message in error, try re-running with 'dym -- subcm'
@ -69,6 +70,7 @@ For more information try --help";
#[cfg(feature = "suggestions")]
static DYM_SUBCMD_AMBIGUOUS: &str = "error: The subcommand 'te' wasn't recognized
Did you mean 'test' or 'temp'?
If you believe you received this message in error, try re-running with 'dym -- te'
@ -81,7 +83,9 @@ For more information try --help";
#[cfg(feature = "suggestions")]
static DYM_ARG: &str =
"error: Found argument '--subcm' which wasn't expected, or isn't valid in this context
\tDid you mean to put '--subcmdarg' after the subcommand 'subcmd'?
Did you mean to put '--subcmdarg' after the subcommand 'subcmd'?
If you tried to supply `--subcm` as a PATTERN use `-- --subcm`
USAGE:

View file

@ -34,7 +34,7 @@ pub fn compare_output(l: App, args: &str, right: &str, stderr: bool) -> bool {
let mut buf = Cursor::new(Vec::with_capacity(50));
let res = l.try_get_matches_from(args.split(' ').collect::<Vec<_>>());
let err = res.unwrap_err();
err.write_to(&mut buf).unwrap();
write!(&mut buf, "{}", err).unwrap();
let content = buf.into_inner();
let left = String::from_utf8(content).unwrap();
assert_eq!(
@ -51,7 +51,7 @@ pub fn compare_output2(l: App, args: &str, right1: &str, right2: &str, stderr: b
let mut buf = Cursor::new(Vec::with_capacity(50));
let res = l.try_get_matches_from(args.split(' ').collect::<Vec<_>>());
let err = res.unwrap_err();
err.write_to(&mut buf).unwrap();
write!(&mut buf, "{}", err).unwrap();
let content = buf.into_inner();
let left = String::from_utf8(content).unwrap();
assert_eq!(

View file

@ -17,7 +17,7 @@ fn version_short() {
assert!(m.is_err());
let err = m.unwrap_err();
assert_eq!(err.kind, ErrorKind::VersionDisplayed);
assert_eq!(err.message, "test 1.3");
assert_eq!(err.to_string(), "test 1.3");
}
#[test]
@ -31,7 +31,7 @@ fn version_long() {
assert!(m.is_err());
let err = m.unwrap_err();
assert_eq!(err.kind, ErrorKind::VersionDisplayed);
assert_eq!(err.message, "test 1.3");
assert_eq!(err.to_string(), "test 1.3");
}
#[test]