mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
feat: allows specifying AllowLeadingHyphen style values, but only for specific args vice command wide
One can now use `Arg::allow_hyphen_values(true)` which will enable `--opt -val` style values only for the specific arg and not command wide. Closes #742
This commit is contained in:
parent
cf9d6ce5cd
commit
c0d70febad
10 changed files with 74 additions and 109 deletions
3
justfile
3
justfile
|
@ -11,6 +11,9 @@
|
|||
run-test TEST:
|
||||
cargo test --test {{TEST}}
|
||||
|
||||
debug TEST:
|
||||
cargo test --test {{TEST}} --features debug
|
||||
|
||||
run-tests:
|
||||
cargo test --features "yaml unstable"
|
||||
|
||||
|
|
|
@ -142,9 +142,7 @@ macro_rules! parse_positional {
|
|||
$pos_counter == $_self.positionals.len()) {
|
||||
$_self.trailing_vals = true;
|
||||
}
|
||||
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
|
||||
return Err(e);
|
||||
}
|
||||
try!($_self.add_val_to_arg($p, &$arg_os, $matcher));
|
||||
|
||||
$matcher.inc_occurrence_of($p.b.name);
|
||||
let _ = $_self.groups_for_arg($p.b.name)
|
||||
|
@ -217,7 +215,7 @@ macro_rules! find_name_from {
|
|||
}};
|
||||
}
|
||||
|
||||
// Finds an option by name
|
||||
// Finds an arg by name
|
||||
macro_rules! find_by_name {
|
||||
($_self:ident, $name:expr, $what:ident, $how:ident) => {
|
||||
$_self.$what.$how().find(|o| &o.b.name == $name)
|
||||
|
|
109
src/app/mod.rs
109
src/app/mod.rs
|
@ -79,19 +79,13 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// let prog = App::new("My Program")
|
||||
/// # ;
|
||||
/// ```
|
||||
pub fn new<S: Into<String>>(n: S) -> Self {
|
||||
App { p: Parser::with_name(n.into()) }
|
||||
}
|
||||
pub fn new<S: Into<String>>(n: S) -> Self { App { p: Parser::with_name(n.into()) } }
|
||||
|
||||
/// Get the name of the app
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.p.meta.name
|
||||
}
|
||||
pub fn get_name(&self) -> &str { &self.p.meta.name }
|
||||
|
||||
/// Get the name of the binary
|
||||
pub fn get_bin_name(&self) -> Option<&str> {
|
||||
self.p.meta.bin_name.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
pub fn get_bin_name(&self) -> Option<&str> { self.p.meta.bin_name.as_ref().map(|s| s.as_str()) }
|
||||
|
||||
/// Creates a new instance of an application requiring a name, but uses the [`crate_authors!`]
|
||||
/// and [`crate_version!`] macros to fill in the [`App::author`] and [`App::version`] fields.
|
||||
|
@ -155,9 +149,7 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// [`examples/17_yaml.yml`]: https://github.com/kbknapp/clap-rs/blob/master/examples/17_yaml.yml
|
||||
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
|
||||
#[cfg(feature = "yaml")]
|
||||
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> {
|
||||
App::from(yaml)
|
||||
}
|
||||
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) }
|
||||
|
||||
/// Sets a string of author(s) that will be displayed to the user when they
|
||||
/// request the help information with `--help` or `-h`.
|
||||
|
@ -1193,9 +1185,7 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// .get_matches();
|
||||
/// ```
|
||||
/// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html
|
||||
pub fn get_matches(self) -> ArgMatches<'a> {
|
||||
self.get_matches_from(&mut env::args_os())
|
||||
}
|
||||
pub fn get_matches(self) -> ArgMatches<'a> { self.get_matches_from(&mut env::args_os()) }
|
||||
|
||||
/// Starts the parsing process. This method will return a [`clap::Result`] type instead of exiting
|
||||
/// the process on failed parse. By default this method gets matches from [`env::args_os`]
|
||||
|
@ -1508,78 +1498,37 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
|
|||
}
|
||||
|
||||
impl<'a, 'b> Clone for App<'a, 'b> {
|
||||
fn clone(&self) -> Self {
|
||||
App { p: self.p.clone() }
|
||||
}
|
||||
fn clone(&self) -> Self { App { p: self.p.clone() } }
|
||||
}
|
||||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
||||
fn name(&self) -> &'n str {
|
||||
unreachable!("App struct does not support AnyArg::name, this is a bug!")
|
||||
}
|
||||
fn kind(&self) -> ArgKind {
|
||||
ArgKind::Subcmd
|
||||
}
|
||||
fn overrides(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn requires(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn blacklist(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> {
|
||||
None
|
||||
}
|
||||
fn is_set(&self, _: ArgSettings) -> bool {
|
||||
false
|
||||
}
|
||||
fn id(&self) -> usize { self.p.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Subcmd }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { None }
|
||||
fn requires(&self) -> Option<&[&'e str]> { None }
|
||||
fn blacklist(&self) -> Option<&[&'e str]> { None }
|
||||
fn required_unless(&self) -> Option<&[&'e str]> { None }
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
||||
fn is_set(&self, _: ArgSettings) -> bool { false }
|
||||
fn set(&mut self, _: ArgSettings) {
|
||||
unreachable!("App struct does not support AnyArg::set, this is a bug!")
|
||||
}
|
||||
fn has_switch(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn max_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn num_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||
None
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn short(&self) -> Option<char> {
|
||||
None
|
||||
}
|
||||
fn long(&self) -> Option<&'e str> {
|
||||
None
|
||||
}
|
||||
fn val_delim(&self) -> Option<char> {
|
||||
None
|
||||
}
|
||||
fn takes_value(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn help(&self) -> Option<&'e str> {
|
||||
self.p.meta.about
|
||||
}
|
||||
fn default_val(&self) -> Option<&'n str> {
|
||||
None
|
||||
}
|
||||
fn longest_filter(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn has_switch(&self) -> bool { false }
|
||||
fn max_vals(&self) -> Option<u64> { None }
|
||||
fn num_vals(&self) -> Option<u64> { None }
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> { None }
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
|
||||
fn min_vals(&self) -> Option<u64> { None }
|
||||
fn short(&self) -> Option<char> { None }
|
||||
fn long(&self) -> Option<&'e str> { None }
|
||||
fn val_delim(&self) -> Option<char> { None }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.p.meta.about }
|
||||
fn default_val(&self) -> Option<&'n str> { None }
|
||||
fn longest_filter(&self) -> bool { true }
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||
if let Some(ref aliases) = self.p.meta.aliases {
|
||||
let vis_aliases: Vec<_> =
|
||||
|
@ -1596,7 +1545,5 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
|||
}
|
||||
|
||||
impl<'n, 'e> fmt::Display for App<'n, 'e> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.p.meta.name)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.p.meta.name) }
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use args::ArgKind;
|
|||
#[doc(hidden)]
|
||||
pub trait AnyArg<'n, 'e>: std_fmt::Display {
|
||||
fn name(&self) -> &'n str;
|
||||
fn id(&self) -> usize;
|
||||
fn overrides(&self) -> Option<&[&'e str]>;
|
||||
fn aliases(&self) -> Option<Vec<&'e str>>;
|
||||
fn requires(&self) -> Option<&[&'e str]>;
|
||||
|
|
|
@ -639,6 +639,13 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
|
||||
/// Allows values which start with a leading hyphen (`-`)
|
||||
///
|
||||
/// **WARNING**: When building your CLIs, consider the effects of allowing leading hyphens and
|
||||
/// the user passing in a value that matches a valid short. For example `prog -opt -F` where
|
||||
/// `-F` is supposed to be a value, yet `-F` is *also* a valid short for anther arg. Care should
|
||||
/// should be taken when designing these args. This is compounded by the ability to "stack"
|
||||
/// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid
|
||||
/// shorts.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
|
|
|
@ -49,6 +49,7 @@ impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
|
|||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
||||
fn name(&self) -> &'n str { self.b.name }
|
||||
fn id(&self) -> usize { self.b.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Flag }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
|
||||
|
@ -98,13 +99,13 @@ mod test {
|
|||
#[test]
|
||||
fn flagbuilder_display() {
|
||||
let mut f = FlagBuilder::new("flg");
|
||||
f.settings.set(ArgSettings::Multiple);
|
||||
f.long = Some("flag");
|
||||
f.b.settings.set(ArgSettings::Multiple);
|
||||
f.s.long = Some("flag");
|
||||
|
||||
assert_eq!(&*format!("{}", f), "--flag");
|
||||
|
||||
let mut f2 = FlagBuilder::new("flg");
|
||||
f2.short = Some('f');
|
||||
f2.s.short = Some('f');
|
||||
|
||||
assert_eq!(&*format!("{}", f2), "-f");
|
||||
}
|
||||
|
@ -112,8 +113,8 @@ mod test {
|
|||
#[test]
|
||||
fn flagbuilder_display_single_alias() {
|
||||
let mut f = FlagBuilder::new("flg");
|
||||
f.long = Some("flag");
|
||||
f.aliases = Some(vec![("als", true)]);
|
||||
f.s.long = Some("flag");
|
||||
f.s.aliases = Some(vec![("als", true)]);
|
||||
|
||||
assert_eq!(&*format!("{}", f), "--flag");
|
||||
}
|
||||
|
@ -121,8 +122,8 @@ mod test {
|
|||
#[test]
|
||||
fn flagbuilder_display_multiple_aliases() {
|
||||
let mut f = FlagBuilder::new("flg");
|
||||
f.short = Some('f');
|
||||
f.aliases =
|
||||
f.s.short = Some('f');
|
||||
f.s.aliases =
|
||||
Some(vec![("alias_not_visible", false), ("f2", true), ("f3", true), ("f4", true)]);
|
||||
assert_eq!(&*format!("{}", f), "-f");
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
||||
fn name(&self) -> &'n str { self.b.name }
|
||||
fn id(&self) -> usize { self.b.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Opt }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
|
||||
|
@ -140,8 +141,8 @@ mod test {
|
|||
#[test]
|
||||
fn optbuilder_display1() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.settings.set(ArgSettings::Multiple);
|
||||
o.s.long = Some("option");
|
||||
o.b.settings.set(ArgSettings::Multiple);
|
||||
|
||||
assert_eq!(&*format!("{}", o), "--option <opt>...");
|
||||
}
|
||||
|
@ -153,8 +154,8 @@ mod test {
|
|||
v_names.insert(1, "name");
|
||||
|
||||
let mut o2 = OptBuilder::new("opt");
|
||||
o2.short = Some('o');
|
||||
o2.val_names = Some(v_names);
|
||||
o2.s.short = Some('o');
|
||||
o2.v.val_names = Some(v_names);
|
||||
|
||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||
}
|
||||
|
@ -166,9 +167,9 @@ mod test {
|
|||
v_names.insert(1, "name");
|
||||
|
||||
let mut o2 = OptBuilder::new("opt");
|
||||
o2.short = Some('o');
|
||||
o2.val_names = Some(v_names);
|
||||
o2.settings.set(ArgSettings::Multiple);
|
||||
o2.s.short = Some('o');
|
||||
o2.v.val_names = Some(v_names);
|
||||
o2.b.settings.set(ArgSettings::Multiple);
|
||||
|
||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||
}
|
||||
|
@ -176,8 +177,8 @@ mod test {
|
|||
#[test]
|
||||
fn optbuilder_display_single_alias() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.aliases = Some(vec![("als", true)]);
|
||||
o.s.long = Some("option");
|
||||
o.s.aliases = Some(vec![("als", true)]);
|
||||
|
||||
assert_eq!(&*format!("{}", o), "--option <opt>");
|
||||
}
|
||||
|
@ -185,8 +186,8 @@ mod test {
|
|||
#[test]
|
||||
fn optbuilder_display_multiple_aliases() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.aliases =
|
||||
o.s.long = Some("option");
|
||||
o.s.aliases =
|
||||
Some(vec![("als_not_visible", false), ("als2", true), ("als3", true), ("als4", true)]);
|
||||
assert_eq!(&*format!("{}", o), "--option <opt>");
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
|||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
|
||||
fn name(&self) -> &'n str { self.b.name }
|
||||
fn id(&self) -> usize { self.b.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Pos }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
|
||||
|
@ -134,7 +135,7 @@ mod test {
|
|||
#[test]
|
||||
fn display_mult() {
|
||||
let mut p = PosBuilder::new("pos", 1);
|
||||
p.settings.set(ArgSettings::Multiple);
|
||||
p.b.settings.set(ArgSettings::Multiple);
|
||||
|
||||
assert_eq!(&*format!("{}", p), "<pos>...");
|
||||
}
|
||||
|
@ -142,7 +143,7 @@ mod test {
|
|||
#[test]
|
||||
fn display_required() {
|
||||
let mut p2 = PosBuilder::new("pos", 1);
|
||||
p2.settings.set(ArgSettings::Required);
|
||||
p2.b.settings.set(ArgSettings::Required);
|
||||
|
||||
assert_eq!(&*format!("{}", p2), "<pos>");
|
||||
}
|
||||
|
@ -153,7 +154,7 @@ mod test {
|
|||
let mut vm = VecMap::new();
|
||||
vm.insert(0, "file1");
|
||||
vm.insert(1, "file2");
|
||||
p2.val_names = Some(vm);
|
||||
p2.v.val_names = Some(vm);
|
||||
|
||||
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
||||
}
|
||||
|
@ -161,11 +162,11 @@ mod test {
|
|||
#[test]
|
||||
fn display_val_names_req() {
|
||||
let mut p2 = PosBuilder::new("pos", 1);
|
||||
p2.settings.set(ArgSettings::Required);
|
||||
p2.b.settings.set(ArgSettings::Required);
|
||||
let mut vm = VecMap::new();
|
||||
vm.insert(0, "file1");
|
||||
vm.insert(1, "file2");
|
||||
p2.val_names = Some(vm);
|
||||
p2.v.val_names = Some(vm);
|
||||
|
||||
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ impl<'a> ArgMatcher<'a> {
|
|||
}
|
||||
|
||||
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
|
||||
debugln!("fn=inc_occurrence_of;");
|
||||
if let Some(a) = self.get_mut(arg) {
|
||||
debugln!("+1 to {}'s occurrences", arg);
|
||||
a.occurs += 1;
|
||||
|
@ -90,6 +91,7 @@ impl<'a> ArgMatcher<'a> {
|
|||
}
|
||||
|
||||
pub fn inc_occurrences_of(&mut self, args: &[&'a str]) {
|
||||
debugln!("fn=inc_occurrences_of;");
|
||||
for arg in args {
|
||||
self.inc_occurrence_of(arg);
|
||||
}
|
||||
|
@ -107,16 +109,20 @@ impl<'a> ArgMatcher<'a> {
|
|||
pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool
|
||||
where A: AnyArg<'a, 'b>
|
||||
{
|
||||
debugln!("fn=needs_more_vals;");
|
||||
if let Some(ma) = self.get(o.name()) {
|
||||
if let Some(num) = o.num_vals() {
|
||||
debugln!("num_vals...{}", num);
|
||||
return if o.is_set(ArgSettings::Multiple) {
|
||||
((ma.vals.len() as u64) % num) != 0
|
||||
} else {
|
||||
num != (ma.vals.len() as u64)
|
||||
};
|
||||
} else if let Some(num) = o.max_vals() {
|
||||
debugln!("max_vals...{}", num);
|
||||
return !((ma.vals.len() as u64) > num);
|
||||
} else if o.min_vals().is_some() {
|
||||
debugln!("min_vals...true");
|
||||
return true;
|
||||
}
|
||||
return o.is_set(ArgSettings::Multiple);
|
||||
|
|
|
@ -150,7 +150,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
|||
|
||||
// Then the positional args
|
||||
for arg in p.positionals() {
|
||||
debugln!("iter;arg={}", arg.name);
|
||||
debugln!("iter;arg={}", arg.b.name);
|
||||
let a = format!("\"{name}:{help}\" \\",
|
||||
name = arg.b.name.to_ascii_uppercase(),
|
||||
help = arg.b.help.unwrap_or(""));
|
||||
|
|
Loading…
Reference in a new issue