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

View file

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

View file

@ -102,7 +102,7 @@ macro_rules! validate_multiples {
debugln!("macro=validate_multiples!;"); debugln!("macro=validate_multiples!;");
if $m.contains(&$a.name) && !$a.settings.is_set(ArgSettings::Multiple) { if $m.contains(&$a.name) && !$a.settings.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples // 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 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. /// Adds an [argument] to the list of valid possibilties.
/// ///
/// # Examples /// # Examples

View file

@ -20,7 +20,7 @@ use INVALID_UTF8;
use suggestions; use suggestions;
use INTERNAL_ERROR_MSG; use INTERNAL_ERROR_MSG;
use SubCommand; use SubCommand;
use fmt::Format; use fmt::{Format, ColorWhen};
use osstringext::OsStrExt2; use osstringext::OsStrExt2;
use app::meta::AppMeta; use app::meta::AppMeta;
use args::MatchedArg; use args::MatchedArg;
@ -49,6 +49,7 @@ pub struct Parser<'a, 'b>
help_short: Option<char>, help_short: Option<char>,
version_short: Option<char>, version_short: Option<char>,
settings: AppFlags, settings: AppFlags,
pub g_settings: Vec<AppSettings>,
pub meta: AppMeta<'b>, pub meta: AppMeta<'b>,
} }
@ -68,6 +69,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
groups: HashMap::new(), groups: HashMap::new(),
global_args: vec![], global_args: vec![],
overrides: vec![], overrides: vec![],
g_settings: vec![],
settings: AppFlags::new(), settings: AppFlags::new(),
meta: AppMeta::new(), meta: AppMeta::new(),
} }
@ -217,6 +219,10 @@ impl<'a, 'b> Parser<'a, 'b>
if self.settings.is_set(AppSettings::DeriveDisplayOrder) { if self.settings.is_set(AppSettings::DeriveDisplayOrder) {
subcmd.p.meta.disp_ord = self.subcommands.len(); 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); self.subcommands.push(subcmd);
} }
@ -531,7 +537,8 @@ impl<'a, 'b> Parser<'a, 'b>
self.meta self.meta
.bin_name .bin_name
.as_ref() .as_ref()
.unwrap_or(&self.meta.name))); .unwrap_or(&self.meta.name),
self.color()));
} }
} }
sc.clone() sc.clone()
@ -551,7 +558,8 @@ impl<'a, 'b> Parser<'a, 'b>
.bin_name .bin_name
.as_ref() .as_ref()
.unwrap_or(&self.meta.name), .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 let None = a.to_str() {
if !self.settings.is_set(AppSettings::StrictUtf8) { if !self.settings.is_set(AppSettings::StrictUtf8) {
return Err( 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 { } else {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(), 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 true
}; };
if should_err { 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 { } else {
return Err(Error::empty_value(self.positionals return Err(Error::empty_value(self.positionals
@ -603,7 +612,8 @@ impl<'a, 'b> Parser<'a, 'b>
.filter(|p| &p.name == &a) .filter(|p| &p.name == &a)
.next() .next()
.expect(INTERNAL_ERROR_MSG), .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)); try!(self.parse_subcommand(sc_name, matcher, it));
} else if self.is_set(AppSettings::SubcommandRequired) { } else if self.is_set(AppSettings::SubcommandRequired) {
let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name); 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) { } else if self.is_set(AppSettings::SubcommandRequiredElseHelp) {
let mut out = vec![]; let mut out = vec![];
try!(self.write_help(&mut out)); try!(self.write_help_err(&mut out));
return Err(Error { return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(), message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand, kind: ErrorKind::MissingArgumentOrSubcommand,
@ -652,7 +662,7 @@ impl<'a, 'b> Parser<'a, 'b>
if matcher.is_empty() && matcher.subcommand_name().is_none() && if matcher.is_empty() && matcher.subcommand_name().is_none() &&
self.is_set(AppSettings::ArgRequiredElseHelp) { self.is_set(AppSettings::ArgRequiredElseHelp) {
let mut out = vec![]; let mut out = vec![];
try!(self.write_help(&mut out)); try!(self.write_help_err(&mut out));
return Err(Error { return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(), message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand, kind: ErrorKind::MissingArgumentOrSubcommand,
@ -1074,7 +1084,8 @@ impl<'a, 'b> Parser<'a, 'b>
arg.push(c); arg.push(c);
return Err(Error::unknown_argument(&*arg, return Err(Error::unknown_argument(&*arg,
"", "",
&*self.create_current_usage(matcher))); &*self.create_current_usage(matcher),
self.color()));
} }
} }
Ok(None) Ok(None)
@ -1093,7 +1104,7 @@ impl<'a, 'b> Parser<'a, 'b>
let v = fv.trim_left_matches(b'='); let v = fv.trim_left_matches(b'=');
if !opt.is_set(ArgSettings::EmptyValues) && v.len_() == 0 { if !opt.is_set(ArgSettings::EmptyValues) && v.len_() == 0 {
sdebugln!("Found Empty - Error"); 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_()); sdebugln!("Found - {:?}, len: {}", v, v.len_());
try!(self.add_val_to_arg(opt, v, matcher)); 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); debugln!("fn=validate_value; val={:?}", val);
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() { 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() { if let Some(ref p_vals) = arg.possible_vals() {
let val_str = val.to_string_lossy(); let val_str = val.to_string_lossy();
@ -1173,16 +1184,17 @@ impl<'a, 'b> Parser<'a, 'b>
return Err(Error::invalid_value(val_str, return Err(Error::invalid_value(val_str,
p_vals, p_vals,
arg, arg,
&*self.create_current_usage(matcher))); &*self.create_current_usage(matcher),
self.color()));
} }
} }
if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() && if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() &&
matcher.contains(&*arg.name()) { 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 Some(ref vtor) = arg.validator() {
if let Err(e) = vtor(val.to_string_lossy().into_owned()) { 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) { if matcher.needs_more_vals(arg) {
@ -1216,19 +1228,19 @@ impl<'a, 'b> Parser<'a, 'b>
let usg = $me.create_current_usage($matcher); let usg = $me.create_current_usage($matcher);
if let Some(f) = $me.flags.iter().filter(|f| f.name == $name).next() { if let Some(f) = $me.flags.iter().filter(|f| f.name == $name).next() {
debugln!("It was a flag..."); 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() } else if let Some(o) = $me.opts.iter()
.filter(|o| o.name == $name) .filter(|o| o.name == $name)
.next() { .next() {
debugln!("It was an option..."); debugln!("It was an option...");
Error::argument_conflict(o, c_with, &*usg) Error::argument_conflict(o, c_with, &*usg, self.color())
} else { } else {
match $me.positionals.values() match $me.positionals.values()
.filter(|p| p.name == $name) .filter(|p| p.name == $name)
.next() { .next() {
Some(p) => { Some(p) => {
debugln!("It was a positional..."); 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) None => panic!(INTERNAL_ERROR_MSG)
} }
@ -1304,7 +1316,8 @@ impl<'a, 'b> Parser<'a, 'b>
} else { } else {
"ere" "ere"
}, },
&*self.create_current_usage(matcher))); &*self.create_current_usage(matcher),
self.color()));
} }
} }
if let Some(num) = a.max_vals() { if let Some(num) = a.max_vals() {
@ -1320,7 +1333,8 @@ impl<'a, 'b> Parser<'a, 'b>
.to_str() .to_str()
.expect(INVALID_UTF8), .expect(INVALID_UTF8),
a, a,
&*self.create_current_usage(matcher))); &*self.create_current_usage(matcher),
self.color()));
} }
} }
if let Some(num) = a.min_vals() { if let Some(num) = a.min_vals() {
@ -1330,7 +1344,8 @@ impl<'a, 'b> Parser<'a, 'b>
return Err(Error::too_few_values(a, return Err(Error::too_few_values(a,
num, num,
ma.vals.len(), ma.vals.len(),
&*self.create_current_usage(matcher))); &*self.create_current_usage(matcher),
self.color()));
} }
} }
Ok(()) Ok(())
@ -1375,7 +1390,8 @@ impl<'a, 'b> Parser<'a, 'b>
.iter() .iter()
.fold(String::new(), .fold(String::new(),
|acc, s| acc + &format!("\n {}", Format::Error(s))[..]), |acc, s| acc + &format!("\n {}", Format::Error(s))[..]),
&*self.create_current_usage(matcher)) &*self.create_current_usage(matcher),
self.color())
}; };
return Err(err); return Err(err);
} }
@ -1439,7 +1455,7 @@ impl<'a, 'b> Parser<'a, 'b>
} }
let used_arg = format!("--{}", arg); 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 // 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() && if self.has_positionals() &&
self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) { 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) 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<()> { fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
macro_rules! add_val { macro_rules! add_val {
($_self:ident, $a:ident, $m:ident) => { ($_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> { pub fn iter_positionals(&self) -> vec_map::Values<PosBuilder> {
self.positionals.values() 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> 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, help_short: self.help_short,
version_short: self.version_short, version_short: self.version_short,
settings: self.settings.clone(), settings: self.settings.clone(),
g_settings: self.g_settings.clone(),
meta: self.meta.clone(), meta: self.meta.clone(),
} }
} }

View file

@ -3,29 +3,32 @@ use std::ascii::AsciiExt;
bitflags! { bitflags! {
flags Flags: u32 { flags Flags: u32 {
const SC_NEGATE_REQS = 0b00000000000000000000001, const SC_NEGATE_REQS = 0b00000000000000000000000001,
const SC_REQUIRED = 0b00000000000000000000010, const SC_REQUIRED = 0b00000000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000100, const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000100,
const GLOBAL_VERSION = 0b00000000000000000001000, const GLOBAL_VERSION = 0b00000000000000000000001000,
const VERSIONLESS_SC = 0b00000000000000000010000, const VERSIONLESS_SC = 0b00000000000000000000010000,
const UNIFIED_HELP = 0b00000000000000000100000, const UNIFIED_HELP = 0b00000000000000000000100000,
const WAIT_ON_ERROR = 0b00000000000000001000000, const WAIT_ON_ERROR = 0b00000000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b00000000000000010000000, const SC_REQUIRED_ELSE_HELP= 0b00000000000000000010000000,
const NEEDS_LONG_HELP = 0b00000000000000100000000, const NEEDS_LONG_HELP = 0b00000000000000000100000000,
const NEEDS_LONG_VERSION = 0b00000000000001000000000, const NEEDS_LONG_VERSION = 0b00000000000000001000000000,
const NEEDS_SC_HELP = 0b00000000000010000000000, const NEEDS_SC_HELP = 0b00000000000000010000000000,
const DISABLE_VERSION = 0b00000000000100000000000, const DISABLE_VERSION = 0b00000000000000100000000000,
const HIDDEN = 0b00000000001000000000000, const HIDDEN = 0b00000000000001000000000000,
const TRAILING_VARARG = 0b00000000010000000000000, const TRAILING_VARARG = 0b00000000000010000000000000,
const NO_BIN_NAME = 0b00000000100000000000000, const NO_BIN_NAME = 0b00000000000100000000000000,
const ALLOW_UNK_SC = 0b00000001000000000000000, const ALLOW_UNK_SC = 0b00000000001000000000000000,
const UTF8_STRICT = 0b00000010000000000000000, const UTF8_STRICT = 0b00000000010000000000000000,
const UTF8_NONE = 0b00000100000000000000000, const UTF8_NONE = 0b00000000100000000000000000,
const LEADING_HYPHEN = 0b00001000000000000000000, const LEADING_HYPHEN = 0b00000001000000000000000000,
const NO_POS_VALUES = 0b00010000000000000000000, const NO_POS_VALUES = 0b00000010000000000000000000,
const NEXT_LINE_HELP = 0b00100000000000000000000, const NEXT_LINE_HELP = 0b00000100000000000000000000,
const DERIVE_DISP_ORDER = 0b01000000000000000000000, const DERIVE_DISP_ORDER = 0b00001000000000000000000000,
const COLORED_HELP = 0b10000000000000000000000, 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 { impl Default for AppFlags {
fn default() -> Self { 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, HidePossibleValuesInHelp => NO_POS_VALUES,
NextLineHelp => NEXT_LINE_HELP, NextLineHelp => NEXT_LINE_HELP,
ColoredHelp => COLORED_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(); /// .get_matches();
/// ``` /// ```
ColoredHelp, 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)] #[doc(hidden)]
NeedsLongVersion, NeedsLongVersion,
#[doc(hidden)] #[doc(hidden)]

View file

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

View file

@ -5,6 +5,91 @@ use ansi_term::Colour::{Green, Red, Yellow};
#[cfg(all(feature = "color", not(target_os = "windows")))] #[cfg(all(feature = "color", not(target_os = "windows")))]
use ansi_term::ANSIString; 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, /// Defines styles for different types of error messages. Defaults to Error=Red, Warning=Yellow,
/// and Good=Green /// and Good=Green
@ -17,6 +102,8 @@ pub enum Format<T> {
Warning(T), Warning(T),
/// Defines the style used for good values, defaults to Green /// Defines the style used for good values, defaults to Green
Good(T), Good(T),
/// Defines no formatting style
None(T),
} }
#[cfg(all(feature = "color", not(target_os = "windows")))] #[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::Error(ref e) => Red.bold().paint(e.as_ref()),
Format::Warning(ref e) => Yellow.paint(e.as_ref()), Format::Warning(ref e) => Yellow.paint(e.as_ref()),
Format::Good(ref e) => Green.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"))] #[cfg(any(not(feature = "color"), target_os = "windows"))]
impl<T: fmt::Display> Format<T> { impl<T: fmt::Display> Format<T> {
fn format(&self) -> &T { fn format(&self) -> &T {
@ -44,10 +125,19 @@ impl<T: fmt::Display> Format<T> {
Format::Error(ref e) => e, Format::Error(ref e) => e,
Format::Warning(ref e) => e, Format::Warning(ref e) => e,
Format::Good(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"))] #[cfg(any(not(feature = "color"), target_os = "windows"))]
impl<T: fmt::Display> fmt::Display for Format<T> { impl<T: fmt::Display> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -59,6 +149,7 @@ impl<T: fmt::Display> fmt::Display for Format<T> {
mod test { mod test {
use super::Format; use super::Format;
use ansi_term::Colour::{Green, Red, Yellow}; use ansi_term::Colour::{Green, Red, Yellow};
use ansi_term::ANSIString;
#[test] #[test]
fn colored_output() { fn colored_output() {
@ -69,5 +160,7 @@ mod test {
assert_eq!(&*format!("{}", good), &*format!("{}", Green.paint("good"))); assert_eq!(&*format!("{}", good), &*format!("{}", Green.paint("good")));
let warn = Format::Warning("warn"); let warn = Format::Warning("warn");
assert_eq!(&*format!("{}", warn), &*format!("{}", Yellow.paint("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; extern crate ansi_term;
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
extern crate yaml_rust; extern crate yaml_rust;
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] #[cfg(any(feature = "wrap_help", feature = "color"))]
extern crate libc; extern crate libc;
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] #[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
extern crate unicode_width; extern crate unicode_width;

View file

@ -63,11 +63,11 @@ macro_rules! value_t {
match v.parse::<$t>() { match v.parse::<$t>() {
Ok(val) => Ok(val), Ok(val) => Ok(val),
Err(_) => Err(_) =>
Err(::clap::Error::value_validation( Err(::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", v))), format!("The argument '{}' isn't a valid value", v))),
} }
} else { } 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>() { match v.parse::<$t>() {
Ok(val) => val, Ok(val) => val,
Err(_) => Err(_) =>
::clap::Error::value_validation( ::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", v)).exit(), format!("The argument '{}' isn't a valid value", v)).exit(),
} }
} else { } 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>() { match pv.parse::<$t>() {
Ok(rv) => tmp.push(rv), Ok(rv) => tmp.push(rv),
Err(..) => { Err(..) => {
err = Some(::clap::Error::value_validation( err = Some(::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", pv))); format!("The argument '{}' isn't a valid value", pv)));
break break
} }
@ -170,7 +170,7 @@ macro_rules! values_t {
None => Ok(tmp), None => Ok(tmp),
} }
} else { } 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) => { ($m:ident.values_of($v:expr), $t:ty) => {
if let Some(vals) = $m.values_of($v) { if let Some(vals) = $m.values_of($v) {
vals.map(|v| v.parse::<$t>().unwrap_or_else(|_|{ 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() format!("One or more arguments aren't valid values")).exit()
})).collect::<Vec<$t>>() })).collect::<Vec<$t>>()
} else { } 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 tests stuff
USAGE: USAGE:
test [OPTIONS] [ARGS] test [OPTIONS] [arg1]
OPTIONS: OPTIONS:
-f, --flag some flag -f, --flag some flag
@ -125,7 +125,7 @@ Kevin K.
tests stuff tests stuff
USAGE: USAGE:
test [FLAGS] [OPTIONS] [ARGS] test [FLAGS] [OPTIONS] [arg1]
FLAGS: FLAGS:
-h, --help Prints help information -h, --help Prints help information
@ -137,3 +137,48 @@ OPTIONS:
ARGS: ARGS:
<arg1> some pos arg")); <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 tests subcommands
USAGE: USAGE:
subcmd [FLAGS] [OPTIONS] [--] [ARGS] subcmd [FLAGS] [OPTIONS] [--] [scpositional]
FLAGS: FLAGS:
-f, --flag tests flags -f, --flag tests flags

View file

@ -176,3 +176,41 @@ fn default_values_user_value() {
assert!(m.is_present("arg")); assert!(m.is_present("arg"));
assert_eq!(m.value_of("arg").unwrap(), "value"); 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>");
}