mirror of
https://github.com/clap-rs/clap
synced 2024-11-14 16:47:21 +00:00
Merge pull request #1295 from kbknapp/v3-dev
updates v3 to latest v2 changes and fixes
This commit is contained in:
commit
b7704575f7
12 changed files with 108 additions and 100 deletions
11
.github/ISSUE_TEMPLATE.md
vendored
11
.github/ISSUE_TEMPLATE.md
vendored
|
@ -31,3 +31,14 @@ Compile clap with cargo features `"debug"` such as:
|
|||
clap = { version = "2", features = ["debug"] }
|
||||
```
|
||||
The output may be very long, so feel free to link to a gist or attach a text file
|
||||
|
||||
<details>
|
||||
<summary> Debug Output </summary>
|
||||
<pre>
|
||||
<code>
|
||||
|
||||
Paste Debug Output Here
|
||||
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
|
|
38
Cargo.toml
38
Cargo.toml
|
@ -5,12 +5,12 @@ version = "3.0.0-alpha1"
|
|||
authors = ["Kevin K. <kbknapp@gmail.com>"]
|
||||
exclude = [
|
||||
".github/*",
|
||||
"examples/*",
|
||||
"tests/*",
|
||||
"benches/*",
|
||||
"clap-perf/*",
|
||||
"examples/*",
|
||||
"tests/*",
|
||||
"benches/*",
|
||||
"clap-perf/*",
|
||||
"etc/*",
|
||||
"*.png",
|
||||
"*.png",
|
||||
"*.dot",
|
||||
"*.yml",
|
||||
"*.toml",
|
||||
|
@ -18,8 +18,8 @@ exclude = [
|
|||
"clap-test.rs"
|
||||
]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"Cargo.toml",
|
||||
"src/**/*",
|
||||
"Cargo.toml",
|
||||
"README.md"
|
||||
]
|
||||
repository = "https://github.com/kbknapp/clap-rs"
|
||||
|
@ -28,10 +28,10 @@ homepage = "https://clap.rs/"
|
|||
readme = "README.md"
|
||||
license = "MIT"
|
||||
keywords = [
|
||||
"argument",
|
||||
"cli",
|
||||
"arg",
|
||||
"parser",
|
||||
"argument",
|
||||
"cli",
|
||||
"arg",
|
||||
"parser",
|
||||
"parse"
|
||||
]
|
||||
categories = ["command-line-interface"]
|
||||
|
@ -50,8 +50,8 @@ maintenance = {status = "actively-developed"}
|
|||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
unicode-width = "0.1.4"
|
||||
textwrap = "0.9.0"
|
||||
ordermap = "0.3.5"
|
||||
textwrap = "0.10.0"
|
||||
indexmap = "1.0.1"
|
||||
strsim = { version = "0.7.0", optional = true }
|
||||
yaml-rust = { version = "0.3.5", optional = true }
|
||||
clippy = { version = "~0.0.166", optional = true }
|
||||
|
@ -60,10 +60,10 @@ vec_map = { version = "0.8", optional = true }
|
|||
term_size = { version = "1.0.0-beta1", optional = true }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
ansi_term = { version = "0.10.0", optional = true }
|
||||
ansi_term = { version = "0.11.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
regex = "0.2"
|
||||
regex = "1.0"
|
||||
lazy_static = "1"
|
||||
version-sync = "0.5"
|
||||
|
||||
|
@ -112,13 +112,5 @@ lto = true
|
|||
debug-assertions = false
|
||||
codegen-units = 1
|
||||
|
||||
[profile.doc]
|
||||
opt-level = 0
|
||||
debug = true
|
||||
rpath = false
|
||||
lto = false
|
||||
debug-assertions = true
|
||||
codegen-units = 4
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["doc"]
|
||||
|
|
|
@ -28,10 +28,10 @@ fn main() {
|
|||
// requires_all(Vec<&str>)
|
||||
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||
// user uses -a, they CANNOT use 'output'"
|
||||
// also has a mutually_excludes_all(Vec<&str>)
|
||||
// also has a conflicts_with_all(Vec<&str>)
|
||||
)
|
||||
// NOTE: In order to compile this example, comment out requres() and
|
||||
// mutually_excludes() because we have not defined an "output" or "config"
|
||||
// NOTE: In order to compile this example, comment out requires() and
|
||||
// conflicts_with() because we have not defined an "output" or "config"
|
||||
// argument.
|
||||
.get_matches();
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ fn main() {
|
|||
// requires_all(Vec<&str>)
|
||||
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||
// user uses -a, they CANNOT use 'output'"
|
||||
// also has a mutually_excludes_all(Vec<&str>)
|
||||
// also has a conflicts_with_all(Vec<&str>)
|
||||
.required(true) // By default this argument MUST be present
|
||||
// NOTE: mutual exclusions take precedence over
|
||||
// required arguments
|
||||
|
@ -37,7 +37,7 @@ fn main() {
|
|||
// Note, we also do not need to specify requires("input")
|
||||
// because requires lists are automatically two-way
|
||||
|
||||
// NOTE: In order to compile this example, comment out mutually_excludes()
|
||||
// NOTE: In order to compile this example, comment out conflicts_with()
|
||||
// because we have not defined an "output" argument.
|
||||
.get_matches();
|
||||
|
||||
|
|
|
@ -845,20 +845,12 @@ impl<'w> Help<'w> {
|
|||
if let Some(author) = parser.app.author {
|
||||
write_thing!(author)
|
||||
}
|
||||
if self.use_long {
|
||||
if let Some(about) = parser.app.long_about {
|
||||
debugln!("Help::write_default_help: writing long about");
|
||||
write_thing!(about)
|
||||
} else if let Some(about) = parser.app.about {
|
||||
debugln!("Help::write_default_help: writing about");
|
||||
write_thing!(about)
|
||||
}
|
||||
if let Some(about) = parser.app.long_about {
|
||||
debugln!("Help::write_default_help: writing long about");
|
||||
write_thing!(about)
|
||||
} else if let Some(about) = parser.app.about {
|
||||
debugln!("Help::write_default_help: writing about");
|
||||
write_thing!(about)
|
||||
} else if let Some(about) = parser.app.long_about {
|
||||
debugln!("Help::write_default_help: writing long about");
|
||||
write_thing!(about)
|
||||
}
|
||||
|
||||
color!(self, "\nUSAGE:", warning)?;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// Std
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::{self, BufWriter, Write};
|
||||
#[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
|
||||
#[cfg(all(feature = "debug", not(any(target_os = "windows", target_arch = "wasm32"))))]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
#[cfg(all(feature = "debug", any(target_os = "windows", target_arch = "wasm32")))]
|
||||
use osstringext::OsStrExt3;
|
||||
use std::slice::Iter;
|
||||
use std::iter::Peekable;
|
||||
use std::mem;
|
||||
|
@ -384,8 +386,9 @@ where
|
|||
self.unset(AS::ValidNegNumFound);
|
||||
// Is this a new argument, or values from a previous option?
|
||||
let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of);
|
||||
if !self.is_set(AS::TrailingValues) &&
|
||||
arg_os.starts_with(b"--") && arg_os.len_() == 2 && starts_new_arg {
|
||||
if !self.is_set(AS::TrailingValues) && arg_os.starts_with(b"--") && arg_os.len() == 2
|
||||
&& starts_new_arg
|
||||
{
|
||||
debugln!("Parser::get_matches_with: setting TrailingVals=true");
|
||||
self.set(AS::TrailingValues);
|
||||
continue;
|
||||
|
@ -430,7 +433,7 @@ where
|
|||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
|
||||
} else if arg_os.starts_with(b"-") && arg_os.len() != 1 {
|
||||
// Try to parse short args like normal, if AllowLeadingHyphen or
|
||||
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
|
||||
// an error, and instead return Ok(None)
|
||||
|
@ -795,7 +798,7 @@ where
|
|||
// Is this a new argument, or values from a previous option?
|
||||
let mut ret = if arg_os.starts_with(b"--") {
|
||||
debugln!("Parser::is_new_arg: -- found");
|
||||
if arg_os.len_() == 2 && !arg_allows_tac {
|
||||
if arg_os.len() == 2 && !arg_allows_tac {
|
||||
return true; // We have to return true so override everything else
|
||||
} else if arg_allows_tac {
|
||||
return false;
|
||||
|
@ -804,7 +807,7 @@ where
|
|||
} else if arg_os.starts_with(b"-") {
|
||||
debugln!("Parser::is_new_arg: - found");
|
||||
// a singe '-' by itself is a value and typically means "stdin" on unix systems
|
||||
!(arg_os.len_() == 1)
|
||||
!(arg_os.len() == 1)
|
||||
} else {
|
||||
debugln!("Parser::is_new_arg: probably value");
|
||||
false
|
||||
|
@ -928,21 +931,21 @@ where
|
|||
|
||||
fn use_long_help(&self) -> bool {
|
||||
debugln!("Parser::use_long_help;");
|
||||
// In this case, both must be checked. This allows the retention of
|
||||
// In this case, both must be checked. This allows the retention of
|
||||
// original formatting, but also ensures that the actual -h or --help
|
||||
// specified by the user is sent through. If HiddenShortHelp is not included,
|
||||
// then items specified with hidden_short_help will also be hidden.
|
||||
let should_long = |v: &Arg| {
|
||||
v.long_help.is_some() ||
|
||||
v.is_set(ArgSettings::HiddenLongHelp) ||
|
||||
v.is_set(ArgSettings::HiddenShortHelp)
|
||||
v.long_help.is_some() ||
|
||||
v.is_set(ArgSettings::HiddenLongHelp) ||
|
||||
v.is_set(ArgSettings::HiddenShortHelp)
|
||||
};
|
||||
|
||||
self.app.long_about.is_some()
|
||||
|| args!(self.app).any(|f| should_long(&f))
|
||||
|| subcommands!(self.app).any(|s| s.long_about.is_some())
|
||||
}
|
||||
|
||||
|
||||
// fn _help(&self, mut use_long: bool) -> ClapError {
|
||||
// debugln!("Parser::_help: use_long={:?}", use_long && self.use_long_help());
|
||||
// use_long = use_long && self.use_long_help();
|
||||
|
@ -1149,7 +1152,7 @@ where
|
|||
if let Some(fv) = val {
|
||||
has_eq = fv.starts_with(&[b'=']) || had_eq;
|
||||
let v = fv.trim_left_matches(b'=');
|
||||
if !empty_vals && (v.len_() == 0 || (needs_eq && !has_eq)) {
|
||||
if !empty_vals && (v.len() == 0 || (needs_eq && !has_eq)) {
|
||||
sdebugln!("Found Empty - Error");
|
||||
return Err(ClapError::empty_value(
|
||||
opt,
|
||||
|
@ -1157,7 +1160,7 @@ where
|
|||
self.app.color(),
|
||||
));
|
||||
}
|
||||
sdebugln!("Found - {:?}, len: {}", v, v.len_());
|
||||
sdebugln!("Found - {:?}, len: {}", v, v.len());
|
||||
debugln!(
|
||||
"Parser::parse_opt: {:?} contains '='...{:?}",
|
||||
fv,
|
||||
|
@ -1208,7 +1211,7 @@ where
|
|||
);
|
||||
if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) {
|
||||
if let Some(delim) = arg.val_delim {
|
||||
if val.is_empty_() {
|
||||
if val.is_empty() {
|
||||
Ok(self.add_single_val_to_arg(arg, val, matcher)?)
|
||||
} else {
|
||||
let mut iret = ParseResult::ValuesDone;
|
||||
|
|
|
@ -9,7 +9,6 @@ use args::{Arg, ArgMatcher, MatchedArg};
|
|||
use args::settings::ArgSettings;
|
||||
use errors::{Error, ErrorKind};
|
||||
use errors::Result as ClapResult;
|
||||
use osstringext::OsStrExt2;
|
||||
use app::settings::AppSettings as AS;
|
||||
use app::parser::{ParseResult, Parser};
|
||||
use fmt::{Colorizer, ColorizerOption};
|
||||
|
@ -110,7 +109,7 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
|
|||
));
|
||||
}
|
||||
}
|
||||
if !arg.is_set(ArgSettings::AllowEmptyValues) && val.is_empty_()
|
||||
if !arg.is_set(ArgSettings::AllowEmptyValues) && val.is_empty()
|
||||
&& matcher.contains(&*arg.name)
|
||||
{
|
||||
debugln!("Validator::validate_arg_values: illegal empty val found");
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
|||
use std::mem;
|
||||
|
||||
// Third Party
|
||||
use ordermap;
|
||||
use indexmap;
|
||||
|
||||
// Internal
|
||||
use args::{Arg, ArgMatches, MatchedArg, SubCommand};
|
||||
|
@ -90,9 +90,9 @@ impl<'a> ArgMatcher<'a> {
|
|||
|
||||
pub fn usage(&mut self, usage: String) { self.0.usage = Some(usage); }
|
||||
|
||||
pub fn arg_names(&'a self) -> ordermap::Keys<&'a str, MatchedArg> { self.0.args.keys() }
|
||||
pub fn arg_names(&'a self) -> indexmap::map::Keys<&'a str, MatchedArg> { self.0.args.keys() }
|
||||
|
||||
pub fn entry(&mut self, arg: &'a str) -> ordermap::Entry<&'a str, MatchedArg> {
|
||||
pub fn entry(&mut self, arg: &'a str) -> indexmap::map::Entry<&'a str, MatchedArg> {
|
||||
self.0.args.entry(arg)
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ impl<'a> ArgMatcher<'a> {
|
|||
|
||||
pub fn subcommand_name(&self) -> Option<&str> { self.0.subcommand_name() }
|
||||
|
||||
pub fn iter(&self) -> ordermap::Iter<&str, MatchedArg> { self.0.args.iter() }
|
||||
pub fn iter(&self) -> indexmap::map::Iter<&str, MatchedArg> { self.0.args.iter() }
|
||||
|
||||
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
|
||||
debugln!("ArgMatcher::inc_occurrence_of: arg={}", arg);
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::iter::Map;
|
|||
use std::slice::Iter;
|
||||
|
||||
// Third Party
|
||||
use ordermap::OrderMap;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
// Internal
|
||||
use INVALID_UTF8;
|
||||
|
@ -62,7 +62,7 @@ use args::SubCommand;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct ArgMatches<'a> {
|
||||
#[doc(hidden)]
|
||||
pub args: OrderMap<&'a str, MatchedArg>,
|
||||
pub args: IndexMap<&'a str, MatchedArg>,
|
||||
#[doc(hidden)]
|
||||
pub subcommand: Option<Box<SubCommand<'a>>>,
|
||||
#[doc(hidden)]
|
||||
|
@ -72,7 +72,7 @@ pub struct ArgMatches<'a> {
|
|||
impl<'a> Default for ArgMatches<'a> {
|
||||
fn default() -> Self {
|
||||
ArgMatches {
|
||||
args: OrderMap::new(),
|
||||
args: IndexMap::new(),
|
||||
subcommand: None,
|
||||
usage: None,
|
||||
}
|
||||
|
|
|
@ -536,7 +536,7 @@ extern crate ansi_term;
|
|||
extern crate atty;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate ordermap;
|
||||
extern crate indexmap;
|
||||
#[cfg(feature = "suggestions")]
|
||||
extern crate strsim;
|
||||
#[cfg(feature = "wrap_help")]
|
||||
|
|
|
@ -16,9 +16,7 @@ pub trait OsStrExt2 {
|
|||
fn split_at_byte(&self, b: u8) -> (&OsStr, &OsStr);
|
||||
fn split_at(&self, i: usize) -> (&OsStr, &OsStr);
|
||||
fn trim_left_matches(&self, b: u8) -> &OsStr;
|
||||
fn len_(&self) -> usize;
|
||||
fn contains_byte(&self, b: u8) -> bool;
|
||||
fn is_empty_(&self) -> bool;
|
||||
fn split(&self, b: u8) -> OsSplit;
|
||||
}
|
||||
|
||||
|
@ -34,8 +32,6 @@ impl OsStrExt3 for OsStr {
|
|||
impl OsStrExt2 for OsStr {
|
||||
fn starts_with(&self, s: &[u8]) -> bool { self.as_bytes().starts_with(s) }
|
||||
|
||||
fn is_empty_(&self) -> bool { self.as_bytes().is_empty() }
|
||||
|
||||
fn contains_byte(&self, byte: u8) -> bool {
|
||||
for b in self.as_bytes() {
|
||||
if b == &byte {
|
||||
|
@ -56,7 +52,7 @@ impl OsStrExt2 for OsStr {
|
|||
}
|
||||
(
|
||||
&*self,
|
||||
OsStr::from_bytes(&self.as_bytes()[self.len_()..self.len_()]),
|
||||
OsStr::from_bytes(&self.as_bytes()[self.len()..self.len()]),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -70,7 +66,7 @@ impl OsStrExt2 for OsStr {
|
|||
}
|
||||
}
|
||||
if found {
|
||||
return OsStr::from_bytes(&self.as_bytes()[self.len_()..]);
|
||||
return OsStr::from_bytes(&self.as_bytes()[self.len()..]);
|
||||
}
|
||||
&*self
|
||||
}
|
||||
|
@ -82,8 +78,6 @@ impl OsStrExt2 for OsStr {
|
|||
)
|
||||
}
|
||||
|
||||
fn len_(&self) -> usize { self.as_bytes().len() }
|
||||
|
||||
fn split(&self, b: u8) -> OsSplit {
|
||||
OsSplit {
|
||||
sep: b,
|
||||
|
@ -118,32 +112,4 @@ impl<'a> Iterator for OsSplit<'a> {
|
|||
}
|
||||
Some(OsStr::from_bytes(&self.val[start..]))
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let mut count = 0;
|
||||
for b in &self.val[self.pos..] {
|
||||
if *b == self.sep {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
return (count, Some(count));
|
||||
}
|
||||
(0, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for OsSplit<'a> {
|
||||
fn next_back(&mut self) -> Option<&'a OsStr> {
|
||||
if self.pos == 0 {
|
||||
return None;
|
||||
}
|
||||
let start = self.pos;
|
||||
for b in self.val[..self.pos].iter().rev() {
|
||||
self.pos -= 1;
|
||||
if *b == self.sep {
|
||||
return Some(OsStr::from_bytes(&self.val[self.pos + 1..start]));
|
||||
}
|
||||
}
|
||||
Some(OsStr::from_bytes(&self.val[..start]))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1443,4 +1443,49 @@ fn multiple_custom_help_headers() {
|
|||
MULTIPLE_CUSTOM_HELP_SECTIONS,
|
||||
false
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
static ISSUE_897: &'static str = "ctest-foo 0.1
|
||||
Long about foo
|
||||
|
||||
USAGE:
|
||||
ctest foo
|
||||
|
||||
FLAGS:
|
||||
-h, --help
|
||||
Prints help information
|
||||
|
||||
-V, --version
|
||||
Prints version information";
|
||||
|
||||
#[test]
|
||||
fn show_long_about_issue_897() {
|
||||
let app = App::new("ctest")
|
||||
.version("0.1")
|
||||
.subcommand(SubCommand::with_name("foo")
|
||||
.version("0.1")
|
||||
.about("About foo")
|
||||
.long_about("Long about foo"));
|
||||
assert!(test::compare_output(app, "ctest foo --help", ISSUE_897, false));
|
||||
}
|
||||
|
||||
static ISSUE_897_SHORT: &'static str = "ctest-foo 0.1
|
||||
Long about foo
|
||||
|
||||
USAGE:
|
||||
ctest foo
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information";
|
||||
|
||||
#[test]
|
||||
fn show_short_about_issue_897() {
|
||||
let app = App::new("ctest")
|
||||
.version("0.1")
|
||||
.subcommand(SubCommand::with_name("foo")
|
||||
.version("0.1")
|
||||
.about("About foo")
|
||||
.long_about("Long about foo"));
|
||||
assert!(test::compare_output(app, "ctest foo -h", ISSUE_897_SHORT, false));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue