Auto merge of #520 - kbknapp:issues-512,518,519, r=kbknapp

Issues 512,518,519
This commit is contained in:
Homu 2016-06-08 10:16:16 +09:00
commit a77c800da7
16 changed files with 802 additions and 187 deletions

147
' Normal file
View file

@ -0,0 +1,147 @@
use std::fmt;
#[cfg(all(feature = "color", not(target_os = "windows")))]
use ansi_term::Colour::{Green, Red, Yellow};
#[cfg(all(feature = "color", not(target_os = "windows")))]
use ansi_term::ANSIString;
#[cfg(color)]
use libc;
#[cfg(color)]
const STDERR: i32 = libc::STDERR_FILENO;
#[cfg(color)]
const STDOUT: i32 = libc::STDOUT_FILENO;
#[cfg(not(color))]
const STDERR: i32 = 0;
#[cfg(not(color))]
const STDOUT: i32 = 0;
#[doc(hidden)]
#[derive(Debug, PartialEq)]
pub enum ColorWhen {
Auto, // Default
Always,
Never
}
#[cfg(color)]
pub fn is_a_tty(stderr: bool) -> bool {
let fd = if stderr { STDERR } else { STDOUT };
unsafe { libc::isatty(fd) != 0 }
}
#[cfg(not(color))]
pub fn is_a_tty(stderr: bool) -> bool {
false
}
#[doc(hidden)]
pub struct Colorizer {
use_stderr: bool,
when: ColorWhen
}
macro_rules! color {
($_self:ident, $c:ident, $m:expr) => {
match $_self.when {
ColorWhen::Auto => if is_a_tty($_self.use_stderr) {
Format::$c($m)
} else {
$m
},
ColorWhen::Always => Format::$c($m),
ColorWhen::Never => $m,
}
};
}
impl Colorizer {
pub fn good<T>(&self, msg: T) -> &fmt::Display where T: fmt::Display {
use Format::Good;
color!(self, Good, msg)
}
pub fn warning<T>(&self, msg: T) -> &fmt::Display where T: fmt::Display {
use Format::Warning;
color!(self, Warning, msg)
}
pub fn error<T>(&self, msg: T) -> &fmt::Display where T: fmt::Display {
use Format::Error;
color!(self, Error, msg)
}
}
impl Default for Colorizer {
fn default() -> Self {
Colorizer {
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)]
#[doc(hidden)]
pub 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),
}
#[cfg(all(feature = "color", not(target_os = "windows")))]
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()),
}
}
}
#[cfg(all(feature = "color", not(target_os = "windows")))]
impl<T: AsRef<str>> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}
#[cfg(any(not(feature = "color"), target_os = "windows"))]
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,
}
}
}
#[cfg(any(not(feature = "color"), target_os = "windows"))]
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", not(target_os = "windows")))]
mod test {
use super::Format;
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")));
}
}

View file

@ -1,7 +1,7 @@
[package]
name = "clap"
version = "2.5.2"
version = "2.6.0"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
@ -27,7 +27,7 @@ regex = "~0.1.69"
[features]
default = ["suggestions", "color", "wrap_help"]
suggestions = ["strsim"]
color = ["ansi_term"]
color = ["ansi_term", "libc"]
yaml = ["yaml-rust"]
wrap_help = ["libc", "unicode-width"]
lints = ["clippy", "nightly"]

View file

@ -497,7 +497,7 @@ features = [ "suggestions", "color" ]
The following is a list of optional `clap` features:
* **"suggestions"**: Turns on the `Did you mean '--myoption' ?` feature for when users make typos. (builds dependency `strsim`)
* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term`)
* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term` and `libc`)
* **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc` and 'unicode-width')
* **"lints"**: This is **not** included by default and should only be used while developing to run basic lints against changes. This can only be used on Rust nightly. (builds dependency `clippy`)
* **"debug"**: This is **not** included by default and should only be used while developing to display debugging information.

View file

@ -10,7 +10,7 @@ use errors::{Error, Result as ClapResult};
use args::{AnyArg, ArgSettings, DispOrder};
use app::{App, AppSettings};
use app::parser::Parser;
use fmt::Format;
use fmt::{Format, Colorizer};
use term;
@ -57,18 +57,18 @@ impl<'b, 'c> DispOrder for App<'b, 'c> {
}
macro_rules! color {
($_self:ident, $nc:expr, $c:ident) => {
($_self:ident, $s:expr, $c:ident) => {
if $_self.color {
write!($_self.writer, "{}", Format::$c($nc))
write!($_self.writer, "{}", $_self.cizer.$c($s))
} else {
write!($_self.writer, "{}", $nc)
write!($_self.writer, "{}", $s)
}
};
($_self:ident, $nc:expr, $i:expr, $c:ident) => {
($_self:ident, $fmt_s:expr, $v:expr, $c:ident) => {
if $_self.color {
write!($_self.writer, "{}", Format::$c(format!($nc, $i)))
write!($_self.writer, "{}", $_self.cizer.$c(format!($fmt_s, $v)))
} else {
write!($_self.writer, $nc, $i)
write!($_self.writer, $fmt_s, $v)
}
};
}
@ -82,12 +82,13 @@ pub struct Help<'a> {
hide_pv: bool,
term_w: Option<usize>,
color: bool,
cizer: Colorizer,
}
// Public Functions
impl<'a> Help<'a> {
/// Create a new `Help` instance.
pub fn new(w: &'a mut Write, next_line_help: bool, hide_pv: bool, color: bool) -> Self {
pub fn new(w: &'a mut Write, next_line_help: bool, hide_pv: bool, color: bool, cizer: Colorizer) -> Self {
debugln!("fn=Help::new;");
Help {
writer: w,
@ -95,6 +96,7 @@ impl<'a> Help<'a> {
hide_pv: hide_pv,
term_w: term::dimensions().map(|(w, _)| w),
color: color,
cizer: cizer,
}
}
@ -108,11 +110,29 @@ impl<'a> Help<'a> {
/// Reads help settings from a Parser
/// and write its help to the wrapped stream.
pub fn write_parser_help(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
debugln!("fn=Help::write_parser_help;");
Self::_write_parser_help(w, parser, false)
}
/// Reads help settings from a Parser
/// and write its help to the wrapped stream which will be stderr. This method prevents
/// formatting when required.
pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
debugln!("fn=Help::write_parser_help;");
Self::_write_parser_help(w, parser, true)
}
#[doc(hidden)]
pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool) -> ClapResult<()> {
debugln!("fn=Help::write_parser_help;");
let nlh = parser.is_set(AppSettings::NextLineHelp);
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
let color = parser.is_set(AppSettings::ColoredHelp);
Self::new(w, nlh, hide_v, color).write_help(&parser)
let cizer = Colorizer {
use_stderr: stderr,
when: parser.color(),
};
Self::new(w, nlh, hide_v, color, cizer).write_help(&parser)
}
/// Writes the parser help to the wrapped stream.
@ -135,7 +155,6 @@ impl<'a> Help<'a> {
fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
{
debugln!("fn=write_args_unsorted;");
let mut longest = 0;
let mut arg_v = Vec::with_capacity(10);
for arg in args.filter(|arg| {
@ -210,7 +229,7 @@ impl<'a> Help<'a> {
debugln!("fn=short;");
try!(write!(self.writer, "{}", TAB));
if let Some(s) = arg.short() {
color!(self, "-{}", s, Good)
color!(self, "-{}", s, good)
} else if arg.has_switch() {
write!(self.writer, "{}", TAB)
} else {
@ -229,7 +248,7 @@ impl<'a> Help<'a> {
if arg.short().is_some() {
try!(write!(self.writer, ", "));
}
try!(color!(self, "--{}", l, Good))
try!(color!(self, "--{}", l, good))
}
try!(write!(self.writer, " "));
} else {
@ -237,7 +256,7 @@ impl<'a> Help<'a> {
if arg.short().is_some() {
try!(write!(self.writer, ", "));
}
try!(color!(self, "--{}", l, Good));
try!(color!(self, "--{}", l, good));
if !self.next_line_help || !arg.is_set(ArgSettings::NextLineHelp) {
write_nspaces!(self.writer, (longest + 4) - (l.len() + 2));
}
@ -260,27 +279,27 @@ impl<'a> Help<'a> {
if let Some(ref vec) = arg.val_names() {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
try!(color!(self, "<{}>", val, Good));
try!(color!(self, "<{}>", val, good));
if it.peek().is_some() {
try!(write!(self.writer, " "));
}
}
let num = vec.len();
if arg.is_set(ArgSettings::Multiple) && num == 1 {
try!(color!(self, "...", Good));
try!(color!(self, "...", good));
}
} else if let Some(num) = arg.num_vals() {
let mut it = (0..num).peekable();
while let Some(_) = it.next() {
try!(color!(self, "<{}>", arg.name(), Good));
try!(color!(self, "<{}>", arg.name(), good));
if it.peek().is_some() {
try!(write!(self.writer, " "));
}
}
} else if arg.has_switch() {
try!(color!(self, "<{}>", arg.name(), Good));
try!(color!(self, "<{}>", arg.name(), good));
} else {
try!(color!(self, "{}", arg, Good));
try!(color!(self, "{}", arg, good));
}
if arg.has_switch() {
if !(self.next_line_help || arg.is_set(ArgSettings::NextLineHelp)) {
@ -421,9 +440,9 @@ impl<'a> Help<'a> {
debugln!("Writing defaults");
return format!(" [default: {}] {}",
if self.color {
format!("{}", Format::Good(pv))
self.cizer.good(pv)
} else {
pv.to_string()
Format::None(pv)
},
if self.hide_pv {
"".into()
@ -432,7 +451,7 @@ impl<'a> Help<'a> {
if self.color {
format!(" [values: {}]",
pv.iter()
.map(|v| format!("{}", Format::Good(v)))
.map(|v| format!("{}", self.cizer.good(v)))
.collect::<Vec<_>>()
.join(", "))
} else {
@ -449,7 +468,7 @@ impl<'a> Help<'a> {
return if self.color {
format!(" [values: {}]",
pv.iter()
.map(|v| format!("{}", Format::Good(v)))
.map(|v| format!("{}", self.cizer.good(v)))
.collect::<Vec<_>>()
.join(", "))
} else {
@ -475,34 +494,46 @@ impl<'a> Help<'a> {
let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
let mut first = true;
if unified_help && (flags || opts) {
let opts_flags = parser.iter_flags()
.map(as_arg_trait)
.chain(parser.iter_opts().map(as_arg_trait));
try!(color!(self, "OPTIONS:\n", Warning));
try!(color!(self, "OPTIONS:\n", warning));
try!(self.write_args(opts_flags));
first = false;
} else {
if flags {
try!(color!(self, "FLAGS:\n", Warning));
try!(color!(self, "FLAGS:\n", warning));
try!(self.write_args(parser.iter_flags()
.map(as_arg_trait)));
first = false;
}
if opts {
try!(self.writer.write(b"\n\n"));
try!(color!(self, "OPTIONS:\n", Warning));
if !first {
try!(self.writer.write(b"\n\n"));
}
try!(color!(self, "OPTIONS:\n", warning));
try!(self.write_args(parser.iter_opts().map(as_arg_trait)));
first = false;
}
}
if pos {
try!(self.writer.write(b"\n\n"));
try!(color!(self, "ARGS:\n", Warning));
if !first {
try!(self.writer.write(b"\n\n"));
}
try!(color!(self, "ARGS:\n", warning));
try!(self.write_args_unsorted(parser.iter_positionals().map(as_arg_trait)));
first = false;
}
if subcmds {
try!(self.writer.write(b"\n\n"));
try!(color!(self, "SUBCOMMANDS:\n", Warning));
if !first {
try!(self.writer.write(b"\n\n"));
}
try!(color!(self, "SUBCOMMANDS:\n", warning));
try!(self.write_subcommands(&parser));
}
@ -511,7 +542,7 @@ impl<'a> Help<'a> {
/// Writes help for subcommands of a Parser Object to the wrapped stream.
fn write_subcommands(&mut self, parser: &Parser) -> io::Result<()> {
debugln!("exec=write_subcommands;");
debugln!("exec=write_subcommands;");
let mut longest = 0;
let mut ord_m = VecMap::new();
@ -525,12 +556,12 @@ impl<'a> Help<'a> {
for (_, btm) in ord_m.into_iter() {
for (_, sc) in btm.into_iter() {
if !first {
debugln!("Writing newline...");
debugln!("Writing newline...");
try!(self.writer.write(b"\n"));
} else {
first = false;
}
debugln!("Writing sc...{}", sc);
debugln!("Writing sc...{}", sc);
try!(self.write_arg(sc, longest));
}
}
@ -548,12 +579,12 @@ impl<'a> Help<'a> {
if let Some(bn) = parser.meta.bin_name.as_ref() {
if bn.contains(' ') {
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
try!(color!(self, bn.replace(" ", "-"), Good))
try!(color!(self, bn.replace(" ", "-"), good))
} else {
try!(color!(self, &parser.meta.name[..], Good))
try!(color!(self, &parser.meta.name[..], good))
}
} else {
try!(color!(self, &parser.meta.name[..], Good))
try!(color!(self, &parser.meta.name[..], good))
}
Ok(())
}
@ -578,7 +609,7 @@ impl<'a> Help<'a> {
try!(write!(self.writer, "{}\n", about));
}
try!(color!(self, "\nUSAGE:", Warning));
try!(color!(self, "\nUSAGE:", warning));
try!(write!(self.writer,
"\n{}{}\n\n",
TAB,
@ -643,7 +674,7 @@ fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> Co
/// Copies the contents of a reader into a writer until a {tag} is found,
/// copying the tag content to a buffer and returning its size.
/// In addition to Errors, there are three possible outputs:
/// In addition to errors, there are three possible outputs:
/// - None: The reader was consumed.
/// - Some(Ok(0)): No tag was captured but the reader still contains data.
/// - Some(Ok(length>0)): a tag with `length` was captured to the tag_buffer.
@ -748,12 +779,12 @@ impl<'a> Help<'a> {
_ => continue,
};
debugln!("iter;tag_buf={};", unsafe {
String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length]
.iter()
.map(|&i|i)
.collect::<Vec<_>>())
});
debugln!("iter;tag_buf={};", unsafe {
String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length]
.iter()
.map(|&i|i)
.collect::<Vec<_>>())
});
match &tag_buf.get_ref()[0..tag_length] {
b"?" => {
try!(self.writer.write(b"Could not decode tag name"));
@ -797,8 +828,8 @@ impl<'a> Help<'a> {
.map(as_arg_trait)));
}
b"positionals" => {
try!(self.write_args_unsorted(parser.iter_positionals()
.map(as_arg_trait)));
try!(self.write_args(parser.iter_positionals()
.map(as_arg_trait)));
}
b"subcommands" => {
try!(self.write_subcommands(&parser));

View file

@ -102,7 +102,7 @@ macro_rules! validate_multiples {
debugln!("macro=validate_multiples!;");
if $m.contains(&$a.name) && !$a.settings.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage($a, &*$_self.create_current_usage($m)))
return Err(Error::unexpected_multiple_usage($a, &*$_self.create_current_usage($m), $_self.color()))
}
};
}

View file

@ -435,6 +435,53 @@ impl<'a, 'b> App<'a, 'b> {
self
}
/// Enables a single setting that is propogated *down* through all child [`SubCommand`]s.
///
/// See [`AppSettings`] for a full list of possibilities and examples.
///
/// **NOTE**: The setting is *only* propogated *down* and not up through parent commands.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .global_setting(AppSettings::SubcommandRequired)
/// # ;
/// ```
/// [`SubCommand`]: ./struct.SubCommand.html
/// [`AppSettings`]: ./enum.AppSettings.html
pub fn global_setting(mut self, setting: AppSettings) -> Self {
self.p.set(setting);
self.p.g_settings.push(setting);
self
}
/// Enables multiple settings which are propogated *down* through all child [`SubCommand`]s.
///
/// See [`AppSettings`] for a full list of possibilities and examples.
///
/// **NOTE**: The setting is *only* propogated *down* and not up through parent commands.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .global_settings(&[AppSettings::SubcommandRequired,
/// AppSettings::ColoredHelp])
/// # ;
/// ```
/// [`SubCommand`]: ./struct.SubCommand.html
/// [`AppSettings`]: ./enum.AppSettings.html
pub fn global_settings(mut self, settings: &[AppSettings]) -> Self {
for s in settings {
self.p.set(*s);
self.p.g_settings.push(*s)
}
self
}
/// Adds an [argument] to the list of valid possibilties.
///
/// # Examples

View file

@ -20,7 +20,7 @@ use INVALID_UTF8;
use suggestions;
use INTERNAL_ERROR_MSG;
use SubCommand;
use fmt::Format;
use fmt::{Format, ColorWhen};
use osstringext::OsStrExt2;
use app::meta::AppMeta;
use args::MatchedArg;
@ -49,6 +49,7 @@ pub struct Parser<'a, 'b>
help_short: Option<char>,
version_short: Option<char>,
settings: AppFlags,
pub g_settings: Vec<AppSettings>,
pub meta: AppMeta<'b>,
}
@ -68,6 +69,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
groups: HashMap::new(),
global_args: vec![],
overrides: vec![],
g_settings: vec![],
settings: AppFlags::new(),
meta: AppMeta::new(),
}
@ -217,6 +219,10 @@ impl<'a, 'b> Parser<'a, 'b>
if self.settings.is_set(AppSettings::DeriveDisplayOrder) {
subcmd.p.meta.disp_ord = self.subcommands.len();
}
for s in &self.g_settings {
subcmd.p.set(*s);
subcmd.p.g_settings.push(*s);
}
self.subcommands.push(subcmd);
}
@ -531,7 +537,8 @@ impl<'a, 'b> Parser<'a, 'b>
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name)));
.unwrap_or(&self.meta.name),
self.color()));
}
}
sc.clone()
@ -551,7 +558,8 @@ impl<'a, 'b> Parser<'a, 'b>
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
@ -565,7 +573,7 @@ impl<'a, 'b> Parser<'a, 'b>
if let None = a.to_str() {
if !self.settings.is_set(AppSettings::StrictUtf8) {
return Err(
Error::invalid_utf8(&*self.create_current_usage(matcher))
Error::invalid_utf8(&*self.create_current_usage(matcher), self.color())
);
}
}
@ -579,7 +587,8 @@ impl<'a, 'b> Parser<'a, 'b>
} else {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
}
@ -595,7 +604,7 @@ impl<'a, 'b> Parser<'a, 'b>
true
};
if should_err {
return Err(Error::empty_value(o, &*self.create_current_usage(matcher)));
return Err(Error::empty_value(o, &*self.create_current_usage(matcher), self.color()));
}
} else {
return Err(Error::empty_value(self.positionals
@ -603,7 +612,8 @@ impl<'a, 'b> Parser<'a, 'b>
.filter(|p| &p.name == &a)
.next()
.expect(INTERNAL_ERROR_MSG),
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
@ -639,10 +649,10 @@ impl<'a, 'b> Parser<'a, 'b>
try!(self.parse_subcommand(sc_name, matcher, it));
} else if self.is_set(AppSettings::SubcommandRequired) {
let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name);
return Err(Error::missing_subcommand(bn, &self.create_current_usage(matcher)));
return Err(Error::missing_subcommand(bn, &self.create_current_usage(matcher), self.color()));
} else if self.is_set(AppSettings::SubcommandRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help(&mut out));
try!(self.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
@ -652,7 +662,7 @@ impl<'a, 'b> Parser<'a, 'b>
if matcher.is_empty() && matcher.subcommand_name().is_none() &&
self.is_set(AppSettings::ArgRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help(&mut out));
try!(self.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
@ -1074,7 +1084,8 @@ impl<'a, 'b> Parser<'a, 'b>
arg.push(c);
return Err(Error::unknown_argument(&*arg,
"",
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
Ok(None)
@ -1093,7 +1104,7 @@ impl<'a, 'b> Parser<'a, 'b>
let v = fv.trim_left_matches(b'=');
if !opt.is_set(ArgSettings::EmptyValues) && v.len_() == 0 {
sdebugln!("Found Empty - Error");
return Err(Error::empty_value(opt, &*self.create_current_usage(matcher)));
return Err(Error::empty_value(opt, &*self.create_current_usage(matcher), self.color()));
}
sdebugln!("Found - {:?}, len: {}", v, v.len_());
try!(self.add_val_to_arg(opt, v, matcher));
@ -1165,7 +1176,7 @@ impl<'a, 'b> Parser<'a, 'b>
{
debugln!("fn=validate_value; val={:?}", val);
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher)));
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), self.color()));
}
if let Some(ref p_vals) = arg.possible_vals() {
let val_str = val.to_string_lossy();
@ -1173,16 +1184,17 @@ impl<'a, 'b> Parser<'a, 'b>
return Err(Error::invalid_value(val_str,
p_vals,
arg,
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() &&
matcher.contains(&*arg.name()) {
return Err(Error::empty_value(arg, &*self.create_current_usage(matcher)));
return Err(Error::empty_value(arg, &*self.create_current_usage(matcher), self.color()));
}
if let Some(ref vtor) = arg.validator() {
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
return Err(Error::value_validation(e));
return Err(Error::value_validation(e, self.color()));
}
}
if matcher.needs_more_vals(arg) {
@ -1216,19 +1228,19 @@ impl<'a, 'b> Parser<'a, 'b>
let usg = $me.create_current_usage($matcher);
if let Some(f) = $me.flags.iter().filter(|f| f.name == $name).next() {
debugln!("It was a flag...");
Error::argument_conflict(f, c_with, &*usg)
Error::argument_conflict(f, c_with, &*usg, self.color())
} else if let Some(o) = $me.opts.iter()
.filter(|o| o.name == $name)
.next() {
debugln!("It was an option...");
Error::argument_conflict(o, c_with, &*usg)
Error::argument_conflict(o, c_with, &*usg, self.color())
} else {
match $me.positionals.values()
.filter(|p| p.name == $name)
.next() {
Some(p) => {
debugln!("It was a positional...");
Error::argument_conflict(p, c_with, &*usg)
Error::argument_conflict(p, c_with, &*usg, self.color())
},
None => panic!(INTERNAL_ERROR_MSG)
}
@ -1304,7 +1316,8 @@ impl<'a, 'b> Parser<'a, 'b>
} else {
"ere"
},
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
if let Some(num) = a.max_vals() {
@ -1320,7 +1333,8 @@ impl<'a, 'b> Parser<'a, 'b>
.to_str()
.expect(INVALID_UTF8),
a,
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
if let Some(num) = a.min_vals() {
@ -1330,7 +1344,8 @@ impl<'a, 'b> Parser<'a, 'b>
return Err(Error::too_few_values(a,
num,
ma.vals.len(),
&*self.create_current_usage(matcher)));
&*self.create_current_usage(matcher),
self.color()));
}
}
Ok(())
@ -1375,7 +1390,8 @@ impl<'a, 'b> Parser<'a, 'b>
.iter()
.fold(String::new(),
|acc, s| acc + &format!("\n {}", Format::Error(s))[..]),
&*self.create_current_usage(matcher))
&*self.create_current_usage(matcher),
self.color())
};
return Err(err);
}
@ -1439,7 +1455,7 @@ impl<'a, 'b> Parser<'a, 'b>
}
let used_arg = format!("--{}", arg);
Err(Error::unknown_argument(&*used_arg, &*suffix.0, &*self.create_current_usage(matcher)))
Err(Error::unknown_argument(&*used_arg, &*suffix.0, &*self.create_current_usage(matcher), self.color()))
}
// Creates a usage string if one was not provided by the user manually. This happens just
@ -1498,7 +1514,17 @@ impl<'a, 'b> Parser<'a, 'b>
}
if self.has_positionals() &&
self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) {
usage.push_str(" [ARGS]");
if self.positionals.len() == 1 {
let p = self.positionals.values().next().expect(INTERNAL_ERROR_MSG);
if !self.groups.values().any(|g| g.args.iter().any(|a| a == &p.name)) {
usage.push_str(&*format!(" [{}]{}", p.name_no_brackets(),
p.multiple_str()));
} else {
usage.push_str(" [ARGS]");
}
} else {
usage.push_str(" [ARGS]");
}
}
@ -1578,6 +1604,10 @@ impl<'a, 'b> Parser<'a, 'b>
Help::write_parser_help(w, &self)
}
pub fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> {
Help::write_parser_help_to_stderr(w, &self)
}
fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
macro_rules! add_val {
($_self:ident, $a:ident, $m:ident) => {
@ -1609,6 +1639,23 @@ impl<'a, 'b> Parser<'a, 'b>
pub fn iter_positionals(&self) -> vec_map::Values<PosBuilder> {
self.positionals.values()
}
// Should we color the output? None=determined by output location, true=yes, false=no
#[doc(hidden)]
pub fn color(&self) -> ColorWhen {
debugln!("exec=color;");
debug!("Color setting...");
if self.is_set(AppSettings::ColorNever) {
sdebugln!("Never");
ColorWhen::Never
} else if self.is_set(AppSettings::ColorAlways) {
sdebugln!("Always");
ColorWhen::Always
} else {
sdebugln!("Auto");
ColorWhen::Auto
}
}
}
impl<'a, 'b> Clone for Parser<'a, 'b>
@ -1630,6 +1677,7 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
help_short: self.help_short,
version_short: self.version_short,
settings: self.settings.clone(),
g_settings: self.g_settings.clone(),
meta: self.meta.clone(),
}
}

View file

@ -3,29 +3,32 @@ use std::ascii::AsciiExt;
bitflags! {
flags Flags: u32 {
const SC_NEGATE_REQS = 0b00000000000000000000001,
const SC_REQUIRED = 0b00000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000100,
const GLOBAL_VERSION = 0b00000000000000000001000,
const VERSIONLESS_SC = 0b00000000000000000010000,
const UNIFIED_HELP = 0b00000000000000000100000,
const WAIT_ON_ERROR = 0b00000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b00000000000000010000000,
const NEEDS_LONG_HELP = 0b00000000000000100000000,
const NEEDS_LONG_VERSION = 0b00000000000001000000000,
const NEEDS_SC_HELP = 0b00000000000010000000000,
const DISABLE_VERSION = 0b00000000000100000000000,
const HIDDEN = 0b00000000001000000000000,
const TRAILING_VARARG = 0b00000000010000000000000,
const NO_BIN_NAME = 0b00000000100000000000000,
const ALLOW_UNK_SC = 0b00000001000000000000000,
const UTF8_STRICT = 0b00000010000000000000000,
const UTF8_NONE = 0b00000100000000000000000,
const LEADING_HYPHEN = 0b00001000000000000000000,
const NO_POS_VALUES = 0b00010000000000000000000,
const NEXT_LINE_HELP = 0b00100000000000000000000,
const DERIVE_DISP_ORDER = 0b01000000000000000000000,
const COLORED_HELP = 0b10000000000000000000000,
const SC_NEGATE_REQS = 0b00000000000000000000000001,
const SC_REQUIRED = 0b00000000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000100,
const GLOBAL_VERSION = 0b00000000000000000000001000,
const VERSIONLESS_SC = 0b00000000000000000000010000,
const UNIFIED_HELP = 0b00000000000000000000100000,
const WAIT_ON_ERROR = 0b00000000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b00000000000000000010000000,
const NEEDS_LONG_HELP = 0b00000000000000000100000000,
const NEEDS_LONG_VERSION = 0b00000000000000001000000000,
const NEEDS_SC_HELP = 0b00000000000000010000000000,
const DISABLE_VERSION = 0b00000000000000100000000000,
const HIDDEN = 0b00000000000001000000000000,
const TRAILING_VARARG = 0b00000000000010000000000000,
const NO_BIN_NAME = 0b00000000000100000000000000,
const ALLOW_UNK_SC = 0b00000000001000000000000000,
const UTF8_STRICT = 0b00000000010000000000000000,
const UTF8_NONE = 0b00000000100000000000000000,
const LEADING_HYPHEN = 0b00000001000000000000000000,
const NO_POS_VALUES = 0b00000010000000000000000000,
const NEXT_LINE_HELP = 0b00000100000000000000000000,
const DERIVE_DISP_ORDER = 0b00001000000000000000000000,
const COLORED_HELP = 0b00010000000000000000000000,
const COLOR_ALWAYS = 0b00100000000000000000000000,
const COLOR_AUTO = 0b01000000000000000000000000,
const COLOR_NEVER = 0b10000000000000000000000000,
}
}
@ -41,7 +44,7 @@ impl Clone for AppFlags {
impl Default for AppFlags {
fn default() -> Self {
AppFlags(NEEDS_LONG_VERSION | NEEDS_LONG_HELP | NEEDS_SC_HELP | UTF8_NONE)
AppFlags(NEEDS_LONG_VERSION | NEEDS_LONG_HELP | NEEDS_SC_HELP | UTF8_NONE | COLOR_AUTO)
}
}
@ -73,7 +76,10 @@ impl AppFlags {
HidePossibleValuesInHelp => NO_POS_VALUES,
NextLineHelp => NEXT_LINE_HELP,
ColoredHelp => COLORED_HELP,
DeriveDisplayOrder => DERIVE_DISP_ORDER
DeriveDisplayOrder => DERIVE_DISP_ORDER,
ColorAlways => COLOR_ALWAYS,
ColorAuto => COLOR_AUTO,
ColorNever => COLOR_NEVER
}
}
@ -492,6 +498,59 @@ pub enum AppSettings {
/// .get_matches();
/// ```
ColoredHelp,
/// Enables colored output only when the output is going to a terminal or TTY.
///
/// **NOTE:** This is the default behavior of `clap`
///
/// **NOTE:** Must be compiled with the `color` cargo feature
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms)
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ColorAuto)
/// .get_matches();
/// ```
ColorAuto,
/// Enables colored output regardless of whether or not the output is going to a terminal/TTY.
///
/// **NOTE:** Must be compiled with the `color` cargo feature
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms)
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ColorAlways)
/// .get_matches();
/// ```
ColorAlways,
/// Disables colored output no matter if the output is going to a terminal/TTY, or not.
///
/// **NOTE:** Must be compiled with the `color` cargo feature
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms)
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ColorNever)
/// .get_matches();
/// ```
ColorNever,
#[doc(hidden)]
NeedsLongVersion,
#[doc(hidden)]

View file

@ -1,6 +1,7 @@
use std::fmt::{Display, Formatter, Result};
use std::result::Result as StdResult;
use std::rc::Rc;
use std::borrow::Cow;
use vec_map::VecMap;
@ -108,6 +109,25 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
}
pb
}
pub fn multiple_str(&self) -> &str {
if self.settings.is_set(ArgSettings::Multiple) && self.val_names.is_none() {
"..."
} else {
""
}
}
pub fn name_no_brackets(&self) -> Cow<str> {
if let Some(ref names) = self.val_names {
Cow::Owned(names.values()
.map(|n| format!("<{}>", n))
.collect::<Vec<_>>()
.join(" "))
} else {
Cow::Borrowed(self.name)
}
}
}
impl<'n, 'e> Display for PosBuilder<'n, 'e> {

View file

@ -6,7 +6,7 @@ use std::io::{self, Write};
use std::convert::From;
use std::result::Result as StdResult;
use fmt::Format;
use fmt;
use suggestions;
use args::any_arg::AnyArg;
@ -14,6 +14,7 @@ use args::any_arg::AnyArg;
/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
pub type Result<T> = StdResult<T, Error>;
/// Command line argument parser kind of error
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ErrorKind {
@ -382,54 +383,62 @@ impl Error {
}
#[doc(hidden)]
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A, other: Option<O>, usage: U) -> Self
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A, other: Option<O>, usage: U, color: fmt::ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display,
O: Into<String>,
U: Display
{
let mut v = vec![arg.name().to_owned()];
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The argument '{}' cannot be used with {}\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(arg.to_string()),
c.error("error:"),
c.warning(&*arg.to_string()),
match other {
Some(name) => {
let n = name.into();
v.push(n.clone());
format!("'{}'", Format::Warning(n))
c.warning(format!("'{}'", n))
}
None => "one or more of the other specified arguments".to_owned(),
None => c.none("one or more of the other specified arguments".to_owned()),
},
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::ArgumentConflict,
info: Some(v),
info: Some(v)
}
}
#[doc(hidden)]
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U) -> Self
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U, color: fmt::ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The argument '{}' requires a value but none was supplied\
\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(arg.to_string()),
c.error("error:"),
c.warning(arg.to_string()),
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::EmptyValue,
info: Some(vec![arg.name().to_owned()]),
}
}
#[doc(hidden)]
pub fn invalid_value<'a, 'b, B, G, A, U>(bad_val: B, good_vals: &[G], arg: &A, usage: U) -> Self
pub fn invalid_value<'a, 'b, B, G, A, U>(bad_val: B, good_vals: &[G], arg: &A, usage: U, color: fmt::ColorWhen) -> Self
where B: AsRef<str>,
G: AsRef<str> + Display,
A: AnyArg<'a, 'b> + Display,
@ -446,32 +455,40 @@ impl Error {
}
sorted.sort();
let valid_values = sorted.join(" ");
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} '{}' isn't a valid value for '{}'\n\t\
[values:{}]\n\
{}\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(bad_val.as_ref()),
Format::Warning(arg.to_string()),
c.error("error:"),
c.warning(bad_val.as_ref()),
c.warning(arg.to_string()),
valid_values,
suffix.0,
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::InvalidValue,
info: Some(vec![arg.name().to_owned(), bad_val.as_ref().to_owned()]),
}
}
#[doc(hidden)]
pub fn invalid_subcommand<S, D, N, U>(subcmd: S, did_you_mean: D, name: N, usage: U) -> Self
pub fn invalid_subcommand<S, D, N, U>(subcmd: S, did_you_mean: D, name: N, usage: U, color: fmt::ColorWhen) -> Self
where S: Into<String>,
D: AsRef<str> + Display,
N: Display,
U: Display
{
let s = subcmd.into();
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The subcommand '{}' wasn't recognized\n\t\
Did you mean '{}' ?\n\n\
@ -479,70 +496,82 @@ impl Error {
re-running with '{} {} {}'\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(&*s),
Format::Good(did_you_mean.as_ref()),
c.error("error:"),
c.warning(&*s),
c.good(did_you_mean.as_ref()),
name,
Format::Good("--"),
c.good("--"),
&*s,
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::InvalidSubcommand,
info: Some(vec![s]),
}
}
#[doc(hidden)]
pub fn unrecognized_subcommand<S, N>(subcmd: S, name: N) -> Self
pub fn unrecognized_subcommand<S, N>(subcmd: S, name: N, color: fmt::ColorWhen) -> Self
where S: Into<String>,
N: Display
{
let s = subcmd.into();
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The subcommand '{}' wasn't recognized\n\n\
USAGE:\n\t\
{} help <subcommands>...\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(&*s),
c.error("error:"),
c.warning(&*s),
name,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::UnrecognizedSubcommand,
info: Some(vec![s]),
}
}
#[doc(hidden)]
pub fn missing_required_argument<R, U>(required: R, usage: U) -> Self
pub fn missing_required_argument<R, U>(required: R, usage: U, color: fmt::ColorWhen) -> Self
where R: Display,
U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The following required arguments were not provided:{}\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
c.error("error:"),
required,
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::MissingRequiredArgument,
info: None,
}
}
#[doc(hidden)]
pub fn missing_subcommand<N, U>(name: N, usage: U) -> Self
pub fn missing_subcommand<N, U>(name: N, usage: U, color: fmt::ColorWhen) -> Self
where N: AsRef<str> + Display,
U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} '{}' requires a subcommand, but one was not provided\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(name),
c.error("error:"),
c.warning(name),
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::MissingSubcommand,
info: None,
}
@ -550,158 +579,208 @@ impl Error {
#[doc(hidden)]
pub fn invalid_utf8<U>(usage: U) -> Self
pub fn invalid_utf8<U>(usage: U, color: fmt::ColorWhen) -> Self
where U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} Invalid UTF-8 was detected in one or more arguments\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
c.error("error:"),
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::InvalidUtf8,
info: None,
}
}
#[doc(hidden)]
pub fn too_many_values<'a, 'b, V, A, U>(val: V, arg: &A, usage: U) -> Self
pub fn too_many_values<'a, 'b, V, A, U>(val: V, arg: &A, usage: U, color: fmt::ColorWhen) -> Self
where V: AsRef<str> + Display + ToOwned,
A: AnyArg<'a, 'b> + Display,
U: Display
{
let v = val.as_ref();
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The value '{}' was provided to '{}', but it wasn't expecting \
any more values\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(v),
Format::Warning(arg.to_string()),
c.error("error:"),
c.warning(v),
c.warning(arg.to_string()),
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::TooManyValues,
info: Some(vec![arg.name().to_owned(), v.to_owned()]),
}
}
#[doc(hidden)]
pub fn too_few_values<'a, 'b, A, U>(arg: &A, min_vals: u64, curr_vals: usize, usage: U) -> Self
pub fn too_few_values<'a, 'b, A, U>(arg: &A, min_vals: u64, curr_vals: usize, usage: U, color: fmt::ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The argument '{}' requires at least {} values, but only {} w{} \
provided\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(arg.to_string()),
Format::Warning(min_vals.to_string()),
Format::Warning(curr_vals.to_string()),
c.error("error:"),
c.warning(arg.to_string()),
c.warning(min_vals.to_string()),
c.warning(curr_vals.to_string()),
if curr_vals > 1 {
"ere"
} else {
"as"
},
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::TooFewValues,
info: Some(vec![arg.name().to_owned()]),
}
}
#[doc(hidden)]
pub fn value_validation(err: String) -> Self {
pub fn value_validation(err: String, color: fmt::ColorWhen) -> Self {
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} {}", Format::Error("error:"), err),
message: format!("{} {}", c.error("error:"), err),
kind: ErrorKind::ValueValidation,
info: None,
}
}
#[doc(hidden)]
pub fn value_validation_auto(err: String) -> Self {
Error::value_validation(err, fmt::ColorWhen::Auto)
}
#[doc(hidden)]
pub fn wrong_number_of_values<'a, 'b, A, S, U>(arg: &A,
num_vals: u64,
curr_vals: usize,
suffix: S,
usage: U)
usage: U, color: fmt::ColorWhen)
-> Self
where A: AnyArg<'a, 'b> + Display,
S: Display,
U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The argument '{}' requires {} values, but {} w{} \
provided\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(arg.to_string()),
Format::Warning(num_vals.to_string()),
Format::Warning(curr_vals.to_string()),
c.error("error:"),
c.warning(arg.to_string()),
c.warning(num_vals.to_string()),
c.warning(curr_vals.to_string()),
suffix,
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::WrongNumberOfValues,
info: Some(vec![arg.name().to_owned()]),
}
}
#[doc(hidden)]
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U) -> Self
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U, color: fmt::ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} The argument '{}' was provided more than once, but cannot \
be used multiple times\n\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(arg.to_string()),
c.error("error:"),
c.warning(arg.to_string()),
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::UnexpectedMultipleUsage,
info: Some(vec![arg.name().to_owned()]),
}
}
#[doc(hidden)]
pub fn unknown_argument<A, U>(arg: A, did_you_mean: &str, usage: U) -> Self
pub fn unknown_argument<A, U>(arg: A, did_you_mean: &str, usage: U, color: fmt::ColorWhen) -> Self
where A: Into<String>,
U: Display
{
let a = arg.into();
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} Found argument '{}' which wasn't expected, or isn't valid in \
this context{}\n\
{}\n\n\
For more information try {}",
Format::Error("error:"),
Format::Warning(&*a),
c.error("error:"),
c.warning(&*a),
if did_you_mean.is_empty() {
"\n".to_owned()
} else {
format!("{}\n", did_you_mean)
},
usage,
Format::Good("--help")),
c.good("--help")),
kind: ErrorKind::UnknownArgument,
info: Some(vec![a]),
}
}
#[doc(hidden)]
pub fn argument_not_found<A>(arg: A) -> Self
pub fn io_error(e: &Error, color: fmt::ColorWhen) -> Self {
let c = fmt::Colorizer {
use_stderr: true,
when: color
};
Error {
message: format!("{} {}", c.error("error:"), e.description()),
kind: ErrorKind::Io,
info: None,
}
}
#[doc(hidden)]
pub fn argument_not_found_auto<A>(arg: A) -> Self
where A: Into<String>
{
let a = arg.into();
let c = fmt::Colorizer {
use_stderr: true,
when: fmt::ColorWhen::Auto
};
Error {
message: format!("{} The argument '{}' wasn't found",
Format::Error("error:"),
c.error("error:"),
a.clone()),
kind: ErrorKind::ArgumentNotFound,
info: Some(vec![a]),
@ -723,8 +802,12 @@ impl Display for Error {
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
let c = fmt::Colorizer {
use_stderr: true,
when: fmt::ColorWhen::Auto
};
Error {
message: format!("{} {}", Format::Error("error:"), e.description()),
message: format!("{} {}", c.error("error:"), e.description()),
kind: ErrorKind::Io,
info: None,
}
@ -733,8 +816,12 @@ impl From<io::Error> for Error {
impl From<std_fmt::Error> for Error {
fn from(e: std_fmt::Error) -> Self {
let c = fmt::Colorizer {
use_stderr: true,
when: fmt::ColorWhen::Auto
};
Error {
message: format!("{} {}", Format::Error("error:"), e),
message: format!("{} {}", c.error("error:"), e),
kind: ErrorKind::Format,
info: None,
}

View file

@ -5,6 +5,91 @@ use ansi_term::Colour::{Green, Red, Yellow};
#[cfg(all(feature = "color", not(target_os = "windows")))]
use ansi_term::ANSIString;
#[cfg(feature = "color")]
use libc;
#[cfg(all(feature = "color", not(target_os = "windows")))]
const STDERR: i32 = libc::STDERR_FILENO;
#[cfg(all(feature = "color", not(target_os = "windows")))]
const STDOUT: i32 = libc::STDOUT_FILENO;
#[cfg(any(not(feature = "color"), target_os = "windows"))]
const STDERR: i32 = 0;
#[cfg(any(not(feature = "color"), target_os = "windows"))]
const STDOUT: i32 = 0;
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ColorWhen {
Auto,
Always,
Never
}
#[cfg(feature = "color")]
pub fn is_a_tty(stderr: bool) -> bool {
debugln!("exec=is_a_tty;");
debugln!("Use stderr...{:?}", stderr);
let fd = if stderr { STDERR } else { STDOUT };
unsafe { libc::isatty(fd) != 0 }
}
#[cfg(not(feature = "color"))]
pub fn is_a_tty(_: bool) -> bool {
debugln!("exec=is_a_tty;");
false
}
#[doc(hidden)]
pub struct Colorizer {
pub use_stderr: bool,
pub when: ColorWhen
}
macro_rules! color {
($_self:ident, $c:ident, $m:expr) => {
match $_self.when {
ColorWhen::Auto => if is_a_tty($_self.use_stderr) {
Format::$c($m)
} else {
Format::None($m)
},
ColorWhen::Always => Format::$c($m),
ColorWhen::Never => Format::None($m),
}
};
}
impl Colorizer {
pub fn good<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str> {
debugln!("exec=good;");
color!(self, Good, msg)
}
pub fn warning<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str> {
debugln!("exec=warning;");
color!(self, Warning, msg)
}
pub fn error<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str> {
debugln!("exec=error;");
color!(self, Error, msg)
}
pub fn none<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str> {
debugln!("exec=none;");
Format::None(msg)
}
}
impl Default for Colorizer {
fn default() -> Self {
Colorizer {
use_stderr: true,
when: ColorWhen::Auto
}
}
}
/// Defines styles for different types of error messages. Defaults to Error=Red, Warning=Yellow,
/// and Good=Green
@ -17,6 +102,8 @@ pub enum Format<T> {
Warning(T),
/// Defines the style used for good values, defaults to Green
Good(T),
/// Defines no formatting style
None(T),
}
#[cfg(all(feature = "color", not(target_os = "windows")))]
@ -26,17 +113,11 @@ impl<T: AsRef<str>> Format<T> {
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(all(feature = "color", not(target_os = "windows")))]
impl<T: AsRef<str>> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}
#[cfg(any(not(feature = "color"), target_os = "windows"))]
impl<T: fmt::Display> Format<T> {
fn format(&self) -> &T {
@ -44,10 +125,19 @@ impl<T: fmt::Display> Format<T> {
Format::Error(ref e) => e,
Format::Warning(ref e) => e,
Format::Good(ref e) => e,
Format::None(ref e) => e,
}
}
}
#[cfg(all(feature = "color", not(target_os = "windows")))]
impl<T: AsRef<str>> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}
#[cfg(any(not(feature = "color"), target_os = "windows"))]
impl<T: fmt::Display> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -59,6 +149,7 @@ impl<T: fmt::Display> fmt::Display for Format<T> {
mod test {
use super::Format;
use ansi_term::Colour::{Green, Red, Yellow};
use ansi_term::ANSIString;
#[test]
fn colored_output() {
@ -69,5 +160,7 @@ mod test {
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")));
}
}

View file

@ -407,7 +407,7 @@ extern crate strsim;
extern crate ansi_term;
#[cfg(feature = "yaml")]
extern crate yaml_rust;
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
#[cfg(any(feature = "wrap_help", feature = "color"))]
extern crate libc;
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
extern crate unicode_width;

View file

@ -63,11 +63,11 @@ macro_rules! value_t {
match v.parse::<$t>() {
Ok(val) => Ok(val),
Err(_) =>
Err(::clap::Error::value_validation(
Err(::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", v))),
}
} else {
Err(::clap::Error::argument_not_found($v))
Err(::clap::Error::argument_not_found_auto($v))
}
};
}
@ -108,11 +108,11 @@ macro_rules! value_t_or_exit {
match v.parse::<$t>() {
Ok(val) => val,
Err(_) =>
::clap::Error::value_validation(
::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", v)).exit(),
}
} else {
::clap::Error::argument_not_found($v).exit()
::clap::Error::argument_not_found_auto($v).exit()
}
};
}
@ -159,7 +159,7 @@ macro_rules! values_t {
match pv.parse::<$t>() {
Ok(rv) => tmp.push(rv),
Err(..) => {
err = Some(::clap::Error::value_validation(
err = Some(::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", pv)));
break
}
@ -170,7 +170,7 @@ macro_rules! values_t {
None => Ok(tmp),
}
} else {
Err(::clap::Error::argument_not_found($v))
Err(::clap::Error::argument_not_found_auto($v))
}
};
}
@ -215,11 +215,11 @@ macro_rules! values_t_or_exit {
($m:ident.values_of($v:expr), $t:ty) => {
if let Some(vals) = $m.values_of($v) {
vals.map(|v| v.parse::<$t>().unwrap_or_else(|_|{
::clap::Error::value_validation(
::clap::Error::value_validation_auto(
format!("One or more arguments aren't valid values")).exit()
})).collect::<Vec<$t>>()
} else {
::clap::Error::argument_not_found($v).exit()
::clap::Error::argument_not_found_auto($v).exit()
}
};
}

View file

@ -93,7 +93,7 @@ Kevin K.
tests stuff
USAGE:
test [OPTIONS] [ARGS]
test [OPTIONS] [arg1]
OPTIONS:
-f, --flag some flag
@ -125,7 +125,7 @@ Kevin K.
tests stuff
USAGE:
test [FLAGS] [OPTIONS] [ARGS]
test [FLAGS] [OPTIONS] [arg1]
FLAGS:
-h, --help Prints help information
@ -137,3 +137,48 @@ OPTIONS:
ARGS:
<arg1> some pos arg"));
}
#[test]
fn global_setting() {
let app = App::new("test")
.global_setting(AppSettings::ColoredHelp)
.subcommand(SubCommand::with_name("subcmd"));
assert!(app.p
.subcommands
.iter()
.filter(|s| s.p
.meta
.name == "subcmd")
.next()
.unwrap()
.p
.is_set(AppSettings::ColoredHelp));
}
#[test]
fn global_settings() {
let app = App::new("test")
.global_settings(&[AppSettings::ColoredHelp, AppSettings::TrailingVarArg])
.subcommand(SubCommand::with_name("subcmd"));
assert!(app.p
.subcommands
.iter()
.filter(|s| s.p
.meta
.name == "subcmd")
.next()
.unwrap()
.p
.is_set(AppSettings::ColoredHelp));
assert!(app.p
.subcommands
.iter()
.filter(|s| s.p
.meta
.name == "subcmd")
.next()
.unwrap()
.p
.is_set(AppSettings::TrailingVarArg));
}

View file

@ -55,7 +55,7 @@ Kevin K. <kbknapp@gmail.com>
tests subcommands
USAGE:
subcmd [FLAGS] [OPTIONS] [--] [ARGS]
subcmd [FLAGS] [OPTIONS] [--] [scpositional]
FLAGS:
-f, --flag tests flags

View file

@ -176,3 +176,41 @@ fn default_values_user_value() {
assert!(m.is_present("arg"));
assert_eq!(m.value_of("arg").unwrap(), "value");
}
#[test]
fn single_positional_usage_string() {
let m = App::new("test").arg_from_usage("[FILE] 'some file'").get_matches_from(vec!["test"]);
assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [FILE]");
}
#[test]
fn single_positional_multiple_usage_string() {
let m = App::new("test").arg_from_usage("[FILE]... 'some file'").get_matches_from(vec!["test"]);
assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [FILE]...");
}
#[test]
fn multiple_positional_usage_string() {
let m = App::new("test")
.arg_from_usage("[FILE] 'some file'")
.arg_from_usage("[FILES]... 'some file'")
.get_matches_from(vec!["test"]);
assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [ARGS]");
}
#[test]
fn multiple_positional_one_required_usage_string() {
let m = App::new("test")
.arg_from_usage("<FILE> 'some file'")
.arg_from_usage("[FILES]... 'some file'")
.get_matches_from(vec!["test", "file"]);
assert_eq!(m.usage(), "USAGE:\n test [FLAGS] <FILE> [ARGS]");
}
#[test]
fn single_positional_required_usage_string() {
let m = App::new("test")
.arg_from_usage("<FILE> 'some file'")
.get_matches_from(vec!["test", "file"]);
assert_eq!(m.usage(), "USAGE:\n test [FLAGS] <FILE>");
}