Auto merge of #647 - kbknapp:issue-638, r=kbknapp

Issue 638
This commit is contained in:
Homu 2016-09-06 06:39:58 +09:00
commit 7e21b443ba
28 changed files with 195 additions and 191 deletions

View file

@ -1,3 +1,21 @@
<a name="v2.11.1"></a>
### v2.11.1 (2016-09-05)
#### Bug Fixes
* **Settings:** fixes an issue where settings weren't propogated down through grand-child subcommands ([b3efc107](https://github.com/kbknapp/clap-rs/commit/b3efc107515d78517b20798ff3890b8a2b04498e), closes [#638](https://github.com/kbknapp/clap-rs/issues/638))
#### Features
* **Errors:** Errors with custom description ([58512f2f](https://github.com/kbknapp/clap-rs/commit/58512f2fcb430745f1ee6ee8f1c67f62dc216c73))
#### Improvements
* **help:** use term_size instead of home-grown solution ([fc7327e9](https://github.com/kbknapp/clap-rs/commit/fc7327e9dcf4258ef2baebf0a8714d9c0622855b))
<a name="v2.11.0"></a>
### v2.11.0 (2016-08-28)

View file

@ -1,7 +1,7 @@
[package]
name = "clap"
version = "2.11.0"
version = "2.11.1"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
repository = "https://github.com/kbknapp/clap-rs.git"
@ -18,7 +18,7 @@ A simple to use, efficient, and full featured Command Line Argument Parser
bitflags = "~0.7"
vec_map = "~0.6"
libc = { version = "~0.2.9", optional = true }
ansi_term = { version = "~0.8.0", optional = true }
ansi_term = { version = "~0.9.0", optional = true }
strsim = { version = "~0.5.1", optional = true }
yaml-rust = { version = "~0.3.2", optional = true }
clippy = { version = "~0.0.88", optional = true }

View file

@ -39,6 +39,14 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New
Here's the highlights for v2.11.1
* Fixes an issue where settings weren't propogated down through grand-child subcommands
* Errors can now have custom description
* Uses `term_size` instead of home-grown solution on Windows
* Updates deps with some minor bug fixes
Here's the highlights for v2.11.0
* Adds the ability to wrap help text intelligently on Windows!

View file

@ -1,4 +1,3 @@
format_strings = false
reorder_imports = true
chain_overflow_last = false
same_line_if_else = true

View file

@ -1,24 +1,24 @@
// Std
// Internal
use app::{App, AppSettings};
use app::parser::Parser;
use args::{AnyArg, ArgSettings, DispOrder};
use errors::{Error, Result as ClapResult};
use fmt::{Format, Colorizer};
use std::cmp;
use std::collections::BTreeMap;
use std::fmt::Display;
use std::io::{self, Cursor, Read, Write};
use std::usize;
// Internal
use app::{App, AppSettings};
use app::parser::Parser;
use args::{AnyArg, ArgSettings, DispOrder};
use errors::{Error, Result as ClapResult};
use fmt::{Format, Colorizer};
// Third Party
use unicode_width::UnicodeWidthStr;
#[cfg(feature = "wrap_help")]
use term_size;
use unicode_segmentation::UnicodeSegmentation;
use vec_map::VecMap;
#[cfg(not(feature = "wrap_help"))]
mod term_size {
pub fn dimensions() -> Option<(usize, usize)> {
@ -26,10 +26,6 @@ mod term_size {
}
}
use unicode_width::UnicodeWidthStr;
// use strext::_StrExt;
fn str_width(s: &str) -> usize {
UnicodeWidthStr::width(s)
}

View file

@ -7,16 +7,6 @@ mod meta;
mod help;
// Std
// Internal
use app::help::Help;
use app::parser::Parser;
use args::{AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings};
use errors::Error;
use errors::Result as ClapResult;
pub use self::settings::AppSettings;
use shell::Shell;
use std::borrow::Borrow;
use std::env;
use std::ffi::OsString;
@ -32,6 +22,15 @@ use vec_map::VecMap;
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
// Internal
use app::help::Help;
use app::parser::Parser;
use args::{AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings};
use errors::Error;
use errors::Result as ClapResult;
pub use self::settings::AppSettings;
use shell::Shell;
/// Used to create a representation of a command line program and all possible command line
/// arguments. Application settings are set using the "builder pattern" with the
/// [`App::get_matches`] family of methods being the terminal methods that starts the
@ -1283,9 +1282,10 @@ impl<'a, 'b> App<'a, 'b> {
{
// Verify all positional assertions pass
self.p.verify_positionals();
// If there are global arguments, we need to propgate them down to subcommands
// If there are global arguments, or settings we need to propgate them down to subcommands
// before parsing incase we run into a subcommand
self.p.propogate_globals();
self.p.propogate_settings();
let mut matcher = ArgMatcher::new();
@ -1379,12 +1379,14 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
if let Some(v) = yaml["display_order"].as_i64() {
a = a.display_order(v as usize);
} else if yaml["display_order"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a u64", yaml["display_order"]);
panic!("Failed to convert YAML value {:?} to a u64",
yaml["display_order"]);
}
if let Some(v) = yaml["setting"].as_str() {
a = a.setting(v.parse().expect("unknown AppSetting found in YAML file"));
} else if yaml["setting"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to an AppSetting", yaml["setting"]);
panic!("Failed to convert YAML value {:?} to an AppSetting",
yaml["setting"]);
}
if let Some(v) = yaml["settings"].as_vec() {
for ys in v {
@ -1396,13 +1398,15 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
if let Some(v) = yaml["settings"].as_str() {
a = a.setting(v.parse().expect("unknown AppSetting found in YAML file"));
} else if yaml["settings"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a string", yaml["settings"]);
panic!("Failed to convert YAML value {:?} to a string",
yaml["settings"]);
}
}
if let Some(v) = yaml["global_setting"].as_str() {
a = a.setting(v.parse().ok().expect("unknown AppSetting found in YAML file"));
} else if yaml["global_setting"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to an AppSetting", yaml["setting"]);
panic!("Failed to convert YAML value {:?} to an AppSetting",
yaml["setting"]);
}
if let Some(v) = yaml["global_settings"].as_vec() {
for ys in v {
@ -1416,7 +1420,8 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
if let Some(v) = yaml["global_settings"].as_str() {
a = a.global_setting(v.parse().expect("unknown AppSetting found in YAML file"));
} else if yaml["global_settings"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a string", yaml["global_settings"]);
panic!("Failed to convert YAML value {:?} to a string",
yaml["global_settings"]);
}
}

View file

@ -1,8 +1,18 @@
// Std
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use std::fs::File;
use std::io::{self, BufWriter, Write};
#[cfg(feature = "debug")]
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::slice::Iter;
// Third Party
use vec_map::{self, VecMap};
// Internal
use INTERNAL_ERROR_MSG;
use INVALID_UTF8;
use SubCommand;
@ -20,20 +30,8 @@ use errors::Result as ClapResult;
use fmt::{Format, ColorWhen};
use osstringext::OsStrExt2;
use shell::Shell;
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use std::fs::File;
use std::io::{self, BufWriter, Write};
#[cfg(feature = "debug")]
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::slice::Iter;
use suggestions;
// Third Party
use vec_map::{self, VecMap};
#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct Parser<'a, 'b>
@ -241,32 +239,39 @@ impl<'a, 'b> Parser<'a, 'b>
sdebugln!("No");
}
debug!("Using Setting VersionlessSubcommands...");
if self.settings.is_set(AppSettings::VersionlessSubcommands) {
sdebugln!("Yes");
subcmd.p.settings.set(AppSettings::DisableVersion);
} else {
sdebugln!("No");
}
debug!("Using Setting GlobalVersion...");
if self.settings.is_set(AppSettings::GlobalVersion) && subcmd.p.meta.version.is_none() &&
self.meta.version.is_some() {
sdebugln!("Yes");
subcmd = subcmd.setting(AppSettings::GlobalVersion)
.version(self.meta.version.unwrap());
} else {
sdebugln!("No");
}
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);
}
pub fn propogate_settings(&mut self) {
for sc in &mut self.subcommands {
// We have to create a new scope in order to tell rustc the borrow of `sc` is
// done and to recursively call this method
{
let vsc = self.settings.is_set(AppSettings::VersionlessSubcommands);
let gv = self.settings.is_set(AppSettings::GlobalVersion);
debugln!("VersionlessSubcommands set...{:?}", vsc);
debugln!("GlobalVersion set...{:?}", gv);
if vsc {
sc.p.settings.set(AppSettings::DisableVersion);
}
if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() {
sc.p.set(AppSettings::GlobalVersion);
sc.p.meta.version = Some(self.meta.version.unwrap());
}
for s in &self.g_settings {
sc.p.set(*s);
sc.p.g_settings.push(*s);
}
}
sc.p.propogate_settings();
}
}
pub fn required(&self) -> Iter<&str> {
self.required.iter()
}

View file

@ -1,4 +1,4 @@
// Std
use std::ascii::AsciiExt;
use std::str::FromStr;

View file

@ -1,11 +1,11 @@
// Third Party
// Std
use std::rc::Rc;
// Third Party
use vec_map::VecMap;
// Internal
use args::settings::ArgSettings;
use std::rc::Rc;
use vec_map::VecMap;
#[doc(hidden)]
pub trait AnyArg<'n, 'e> {

View file

@ -168,27 +168,13 @@ impl<'a, 'b> Arg<'a, 'b> {
"required_unless" => yaml_to_str!(a, v, required_unless),
"display_order" => yaml_to_usize!(a, v, display_order),
"default_value" => yaml_to_str!(a, v, default_value),
"value_names" => {
yaml_vec_or_str!(v, a, value_name)
}
"groups" => {
yaml_vec_or_str!(v, a, group)
}
"requires" => {
yaml_vec_or_str!(v, a, requires)
}
"conflicts_with" => {
yaml_vec_or_str!(v, a, conflicts_with)
}
"overrides_with" => {
yaml_vec_or_str!(v, a, overrides_with)
}
"possible_values" => {
yaml_vec_or_str!(v, a, possible_value)
}
"required_unless_one" => {
yaml_vec_or_str!(v, a, required_unless)
}
"value_names" => yaml_vec_or_str!(v, a, value_name),
"groups" => yaml_vec_or_str!(v, a, group),
"requires" => yaml_vec_or_str!(v, a, requires),
"conflicts_with" => yaml_vec_or_str!(v, a, conflicts_with),
"overrides_with" => yaml_vec_or_str!(v, a, overrides_with),
"possible_values" => yaml_vec_or_str!(v, a, possible_value),
"required_unless_one" => yaml_vec_or_str!(v, a, required_unless),
"required_unless_all" => {
a = yaml_vec_or_str!(v, a, required_unless);
a.setb(ArgSettings::RequiredUnlessAll);
@ -1990,8 +1976,8 @@ impl<'a, 'b> Arg<'a, 'b> {
self = self.set(ArgSettings::TakesValue);
self = self.set(ArgSettings::UseValueDelimiter);
self.val_delim = Some(d.chars()
.nth(0)
.expect("Failed to get value_delimiter from arg"));
.nth(0)
.expect("Failed to get value_delimiter from arg"));
self
}

View file

@ -1,11 +1,4 @@
// Std
// Internal
use Arg;
use args::{AnyArg, DispOrder};
use args::settings::{ArgFlags, ArgSettings};
use std::convert::From;
use std::fmt::{Display, Formatter, Result};
use std::rc::Rc;
@ -14,6 +7,11 @@ use std::result::Result as StdResult;
// Third Party
use vec_map::VecMap;
// Internal
use Arg;
use args::{AnyArg, DispOrder};
use args::settings::{ArgFlags, ArgSettings};
#[derive(Debug)]
#[doc(hidden)]
pub struct FlagBuilder<'n, 'e> {

View file

@ -1,10 +1,4 @@
// Std
// Internal
use args::{AnyArg, Arg, DispOrder};
use args::settings::{ArgFlags, ArgSettings};
use std::fmt::{Display, Formatter, Result};
use std::rc::Rc;
use std::result::Result as StdResult;
@ -12,6 +6,10 @@ use std::result::Result as StdResult;
// Third Party
use vec_map::VecMap;
// Internal
use args::{AnyArg, Arg, DispOrder};
use args::settings::{ArgFlags, ArgSettings};
#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct OptBuilder<'n, 'e> {

View file

@ -1,11 +1,4 @@
// Std
// Internal
use Arg;
use args::{AnyArg, DispOrder};
use args::settings::{ArgFlags, ArgSettings};
use std::borrow::Cow;
use std::fmt::{Display, Formatter, Result};
use std::rc::Rc;
@ -14,6 +7,11 @@ use std::result::Result as StdResult;
// Third Party
use vec_map::VecMap;
// Internal
use Arg;
use args::{AnyArg, DispOrder};
use args::settings::{ArgFlags, ArgSettings};
#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct PosBuilder<'n, 'e> {

View file

@ -1,11 +1,4 @@
// Std
// Internal
use args::{ArgMatches, MatchedArg, SubCommand};
use args::AnyArg;
use args::settings::ArgSettings;
use std::collections::hash_map::{Entry, Iter};
use std::ffi::OsStr;
use std::ops::Deref;
@ -13,6 +6,11 @@ use std::ops::Deref;
// Third Party
use vec_map::VecMap;
// Internal
use args::{ArgMatches, MatchedArg, SubCommand};
use args::AnyArg;
use args::settings::ArgSettings;
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct ArgMatcher<'a>(pub ArgMatches<'a>);

View file

@ -1,11 +1,4 @@
// Std
// Internal
use INVALID_UTF8;
use args::MatchedArg;
use args::SubCommand;
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
@ -15,6 +8,11 @@ use std::slice;
// Third Party
use vec_map;
// Internal
use INVALID_UTF8;
use args::MatchedArg;
use args::SubCommand;
/// Used to get information about the arguments that where supplied to the program at runtime by
/// the user. New instances of this struct are obtained by using the [`App::get_matches`] family of
/// methods.

View file

@ -461,9 +461,9 @@ impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
let name_str = name_yml.as_str().expect("failed to convert arg YAML name to str");
a.name = name_str;
b.get(name_yml)
.expect("failed to get name_str")
.as_hash()
.expect("failed to convert to a hash")
.expect("failed to get name_str")
.as_hash()
.expect("failed to convert to a hash")
} else {
b
};
@ -471,21 +471,15 @@ impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
for (k, v) in group_settings.iter() {
a = match k.as_str().unwrap() {
"required" => a.required(v.as_bool().unwrap()),
"args" => {
yaml_vec_or_str!(v, a, arg)
}
"args" => yaml_vec_or_str!(v, a, arg),
"arg" => {
if let Some(ys) = v.as_str() {
a = a.arg(ys);
}
a
}
"requires" => {
yaml_vec_or_str!(v, a, requires)
}
"conflicts_with" => {
yaml_vec_or_str!(v, a, conflicts_with)
}
"requires" => yaml_vec_or_str!(v, a, requires),
"conflicts_with" => yaml_vec_or_str!(v, a, conflicts_with),
"name" => {
if let Some(ys) = v.as_str() {
a.name = ys;
@ -514,16 +508,16 @@ mod test {
#[test]
fn groups() {
let g = ArgGroup::with_name("test")
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];
@ -537,16 +531,16 @@ mod test {
#[test]
fn test_debug() {
let g = ArgGroup::with_name("test")
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];
@ -569,16 +563,16 @@ mod test {
#[test]
fn test_from() {
let g = ArgGroup::with_name("test")
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];

View file

@ -1,5 +1,7 @@
// Std
use std::ffi::OsString;
// Third Party
use vec_map::VecMap;
#[doc(hidden)]

View file

@ -1,4 +1,3 @@
pub use self::any_arg::{AnyArg, DispOrder};
pub use self::arg::Arg;
pub use self::arg_builder::{FlagBuilder, OptBuilder, PosBuilder};

View file

@ -1,5 +1,4 @@
// Std
use std::ascii::AsciiExt;
use std::str::FromStr;

View file

@ -1,6 +1,8 @@
// Third Party
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
// Internal
use App;
use ArgMatches;

View file

@ -1,12 +1,10 @@
// Std
use std::io::Write;
// Internal
use app::parser::Parser;
use args::{ArgSettings, OptBuilder};
use shell::Shell;
use std::io::Write;
macro_rules! w {
($buf:expr, $to_w:expr) => {

View file

@ -1,9 +1,4 @@
// Std
// Internal
use args::any_arg::AnyArg;
use fmt;
use std::convert::From;
use std::error::Error as StdError;
use std::fmt as std_fmt;
@ -11,13 +6,16 @@ use std::fmt::Display;
use std::io::{self, Write};
use std::process;
use std::result::Result as StdResult;
// Internal
use args::any_arg::AnyArg;
use fmt;
use suggestions;
/// Short hand for [`Result`] type
/// [`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 {

View file

@ -5,7 +5,8 @@ use ansi_term::ANSIString;
use ansi_term::Colour::{Green, Red, Yellow};
#[cfg(feature = "color")]
use libc;use std::fmt;
use libc;
use std::fmt;
#[cfg(all(feature = "color", not(target_os = "windows")))]
const STDERR: i32 = libc::STDERR_FILENO;

View file

@ -389,7 +389,7 @@
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
//trivial_numeric_casts, // While bitflags fails this lint
// trivial_numeric_casts, // While bitflags fails this lint
unused_import_braces,
unused_allocation,
unused_qualifications)]

View file

@ -1,5 +1,6 @@
#[cfg(target_os = "windows")]
use INVALID_UTF8;use std::ffi::OsStr;
use INVALID_UTF8;
use std::ffi::OsStr;
#[cfg(not(target_os = "windows"))]
use std::os::unix::ffi::OsStrExt;

View file

@ -1,6 +1,8 @@
// Third Party
#[cfg(feature = "suggestions")]
use strsim;
// Internal
use fmt::Format;
/// Produces a string from a given list of possible values which is similar to

View file

@ -1,12 +1,10 @@
// Third Party
use vec_map::VecMap;
// Internal
use INTERNAL_ERROR_MSG;
use args::Arg;
use args::settings::ArgSettings;
use vec_map::VecMap;
type ParseResult = Result<(), ()>;

View file

@ -15,10 +15,11 @@ fn sub_command_negate_required() {
#[test]
fn global_version() {
let app = App::new("global_version")
let mut app = App::new("global_version")
.setting(AppSettings::GlobalVersion)
.version("1.1")
.subcommand(SubCommand::with_name("sub1"));
app.p.propogate_settings();
assert_eq!(app.p.subcommands[0].p.meta.version, Some("1.1"));
}
@ -140,9 +141,10 @@ ARGS:
#[test]
fn global_setting() {
let app = App::new("test")
let mut app = App::new("test")
.global_setting(AppSettings::ColoredHelp)
.subcommand(SubCommand::with_name("subcmd"));
app.p.propogate_settings();
assert!(app.p
.subcommands
.iter()
@ -157,9 +159,10 @@ fn global_setting() {
#[test]
fn global_settings() {
let app = App::new("test")
let mut app = App::new("test")
.global_settings(&[AppSettings::ColoredHelp, AppSettings::TrailingVarArg])
.subcommand(SubCommand::with_name("subcmd"));
app.p.propogate_settings();
assert!(app.p
.subcommands
.iter()
@ -262,7 +265,7 @@ fn test_unset_settings() {
let m = App::new("unset_settings");
assert!(&m.p.is_set(AppSettings::AllowInvalidUtf8));
assert!(&m.p.is_set(AppSettings::ColorAuto));
let m = m.unset_settings(&[AppSettings::AllowInvalidUtf8,
AppSettings::ColorAuto]);
assert!(!m.p.is_set(AppSettings::AllowInvalidUtf8));