mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
Merge #1824
1824: Finished color refactor r=CreepySkeleton a=pksunkara Co-authored-by: Pavan Kumar Sunkara <pavan.sss1991@gmail.com>
This commit is contained in:
commit
4c3a7f7998
25 changed files with 861 additions and 941 deletions
|
@ -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"]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(" "))
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()))
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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(),
|
||||
))
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
38
src/util/termcolor.rs
Normal 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(())
|
||||
}
|
||||
}
|
|
@ -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"))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 ");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue