mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 06:42:33 +00:00
Auto merge of #520 - kbknapp:issues-512,518,519, r=kbknapp
Issues 512,518,519
This commit is contained in:
commit
a77c800da7
16 changed files with 802 additions and 187 deletions
147
'
Normal file
147
'
Normal 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")));
|
||||
}
|
||||
}
|
|
@ -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"]
|
||||
|
|
|
@ -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.
|
||||
|
|
123
src/app/help.rs
123
src/app/help.rs
|
@ -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));
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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> {
|
||||
|
|
223
src/errors.rs
223
src/errors.rs
|
@ -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,
|
||||
}
|
||||
|
|
107
src/fmt.rs
107
src/fmt.rs
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue