mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
setting(AllowMissingPositional): allows one to implement $ prog [optional] <required>
Closes #636
This commit is contained in:
parent
ce083919be
commit
6edde30b8e
2 changed files with 109 additions and 50 deletions
|
@ -546,10 +546,6 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
a.b.settings.is_set(ArgSettings::Multiple) &&
|
a.b.settings.is_set(ArgSettings::Multiple) &&
|
||||||
(a.index as usize != self.positionals.len())
|
(a.index as usize != self.positionals.len())
|
||||||
}) {
|
}) {
|
||||||
debug_assert!(self.positionals.values()
|
|
||||||
.filter(|p| p.b.settings.is_set(ArgSettings::Multiple)
|
|
||||||
&& p.v.num_vals.is_none()).map(|_| 1).sum::<u64>() <= 1,
|
|
||||||
"Only one positional argument with .multiple(true) set is allowed per command");
|
|
||||||
|
|
||||||
debug_assert!({
|
debug_assert!({
|
||||||
let mut it = self.positionals.values().rev();
|
let mut it = self.positionals.values().rev();
|
||||||
|
@ -581,17 +577,42 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
|
|
||||||
// If it's required we also need to ensure all previous positionals are
|
// If it's required we also need to ensure all previous positionals are
|
||||||
// required too
|
// required too
|
||||||
let mut found = false;
|
if self.is_set(AppSettings::AllowMissingPositional) {
|
||||||
for p in self.positionals.values().rev() {
|
let mut found = false;
|
||||||
if found {
|
let mut foundx2 = false;
|
||||||
debug_assert!(p.b.settings.is_set(ArgSettings::Required),
|
for p in self.positionals.values().rev() {
|
||||||
"Found positional argument which is not required with a lower index \
|
if foundx2 && !p.b.settings.is_set(ArgSettings::Required) {
|
||||||
than a required positional argument: {:?} index {}",
|
// [arg1] <arg2> is Ok
|
||||||
p.b.name,
|
// [arg1] <arg2> <arg3> Is not
|
||||||
p.index);
|
debug_assert!(p.b.settings.is_set(ArgSettings::Required),
|
||||||
} else if p.b.settings.is_set(ArgSettings::Required) {
|
"Found positional argument which is not required with a lower index \
|
||||||
found = true;
|
than a required positional argument by two or more: {:?} index {}",
|
||||||
continue;
|
p.b.name,
|
||||||
|
p.index);
|
||||||
|
} else if p.b.settings.is_set(ArgSettings::Required) {
|
||||||
|
if found {
|
||||||
|
foundx2 = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut found = false;
|
||||||
|
for p in self.positionals.values().rev() {
|
||||||
|
if found {
|
||||||
|
debug_assert!(p.b.settings.is_set(ArgSettings::Required),
|
||||||
|
"Found positional argument which is not required with a lower index \
|
||||||
|
than a required positional argument: {:?} index {}",
|
||||||
|
p.b.name,
|
||||||
|
p.index);
|
||||||
|
} else if p.b.settings.is_set(ArgSettings::Required) {
|
||||||
|
found = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -861,12 +882,15 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
let low_index_mults = self.is_set(AppSettings::LowIndexMultiplePositional) &&
|
let low_index_mults = self.is_set(AppSettings::LowIndexMultiplePositional) &&
|
||||||
!self.positionals.is_empty() &&
|
!self.positionals.is_empty() &&
|
||||||
pos_counter == (self.positionals.len() - 1);
|
pos_counter == (self.positionals.len() - 1);
|
||||||
|
let missing_pos = self.is_set(AppSettings::AllowMissingPositional) &&
|
||||||
|
!self.positionals.is_empty() &&
|
||||||
|
pos_counter == (self.positionals.len() - 1);
|
||||||
debugln!("Parser::get_matches_with: Low index multiples...{:?}", low_index_mults);
|
debugln!("Parser::get_matches_with: Low index multiples...{:?}", low_index_mults);
|
||||||
debugln!("Parser::get_matches_with: Positional counter...{}", pos_counter);
|
debugln!("Parser::get_matches_with: Positional counter...{}", pos_counter);
|
||||||
if low_index_mults {
|
if low_index_mults || missing_pos {
|
||||||
if let Some(na) = it.peek() {
|
if let Some(na) = it.peek() {
|
||||||
let n = (*na).clone().into();
|
let n = (*na).clone().into();
|
||||||
needs_val_of = if let None = needs_val_of {
|
needs_val_of = if needs_val_of.is_none() {
|
||||||
if let Some(p) = self.positionals.get(pos_counter) {
|
if let Some(p) = self.positionals.get(pos_counter) {
|
||||||
Some(p.b.name)
|
Some(p.b.name)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,39 +5,40 @@ use std::ops::BitOr;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
flags Flags: u64 {
|
flags Flags: u64 {
|
||||||
const SC_NEGATE_REQS = 0b000000000000000000000000000000001,
|
const SC_NEGATE_REQS = 0b0000000000000000000000000000000001,
|
||||||
const SC_REQUIRED = 0b000000000000000000000000000000010,
|
const SC_REQUIRED = 0b0000000000000000000000000000000010,
|
||||||
const A_REQUIRED_ELSE_HELP = 0b000000000000000000000000000000100,
|
const A_REQUIRED_ELSE_HELP = 0b0000000000000000000000000000000100,
|
||||||
const GLOBAL_VERSION = 0b000000000000000000000000000001000,
|
const GLOBAL_VERSION = 0b0000000000000000000000000000001000,
|
||||||
const VERSIONLESS_SC = 0b000000000000000000000000000010000,
|
const VERSIONLESS_SC = 0b0000000000000000000000000000010000,
|
||||||
const UNIFIED_HELP = 0b000000000000000000000000000100000,
|
const UNIFIED_HELP = 0b0000000000000000000000000000100000,
|
||||||
const WAIT_ON_ERROR = 0b000000000000000000000000001000000,
|
const WAIT_ON_ERROR = 0b0000000000000000000000000001000000,
|
||||||
const SC_REQUIRED_ELSE_HELP= 0b000000000000000000000000010000000,
|
const SC_REQUIRED_ELSE_HELP= 0b0000000000000000000000000010000000,
|
||||||
const NEEDS_LONG_HELP = 0b000000000000000000000000100000000,
|
const NEEDS_LONG_HELP = 0b0000000000000000000000000100000000,
|
||||||
const NEEDS_LONG_VERSION = 0b000000000000000000000001000000000,
|
const NEEDS_LONG_VERSION = 0b0000000000000000000000001000000000,
|
||||||
const NEEDS_SC_HELP = 0b000000000000000000000010000000000,
|
const NEEDS_SC_HELP = 0b0000000000000000000000010000000000,
|
||||||
const DISABLE_VERSION = 0b000000000000000000000100000000000,
|
const DISABLE_VERSION = 0b0000000000000000000000100000000000,
|
||||||
const HIDDEN = 0b000000000000000000001000000000000,
|
const HIDDEN = 0b0000000000000000000001000000000000,
|
||||||
const TRAILING_VARARG = 0b000000000000000000010000000000000,
|
const TRAILING_VARARG = 0b0000000000000000000010000000000000,
|
||||||
const NO_BIN_NAME = 0b000000000000000000100000000000000,
|
const NO_BIN_NAME = 0b0000000000000000000100000000000000,
|
||||||
const ALLOW_UNK_SC = 0b000000000000000001000000000000000,
|
const ALLOW_UNK_SC = 0b0000000000000000001000000000000000,
|
||||||
const UTF8_STRICT = 0b000000000000000010000000000000000,
|
const UTF8_STRICT = 0b0000000000000000010000000000000000,
|
||||||
const UTF8_NONE = 0b000000000000000100000000000000000,
|
const UTF8_NONE = 0b0000000000000000100000000000000000,
|
||||||
const LEADING_HYPHEN = 0b000000000000001000000000000000000,
|
const LEADING_HYPHEN = 0b0000000000000001000000000000000000,
|
||||||
const NO_POS_VALUES = 0b000000000000010000000000000000000,
|
const NO_POS_VALUES = 0b0000000000000010000000000000000000,
|
||||||
const NEXT_LINE_HELP = 0b000000000000100000000000000000000,
|
const NEXT_LINE_HELP = 0b0000000000000100000000000000000000,
|
||||||
const DERIVE_DISP_ORDER = 0b000000000001000000000000000000000,
|
const DERIVE_DISP_ORDER = 0b0000000000001000000000000000000000,
|
||||||
const COLORED_HELP = 0b000000000010000000000000000000000,
|
const COLORED_HELP = 0b0000000000010000000000000000000000,
|
||||||
const COLOR_ALWAYS = 0b000000000100000000000000000000000,
|
const COLOR_ALWAYS = 0b0000000000100000000000000000000000,
|
||||||
const COLOR_AUTO = 0b000000001000000000000000000000000,
|
const COLOR_AUTO = 0b0000000001000000000000000000000000,
|
||||||
const COLOR_NEVER = 0b000000010000000000000000000000000,
|
const COLOR_NEVER = 0b0000000010000000000000000000000000,
|
||||||
const DONT_DELIM_TRAIL = 0b000000100000000000000000000000000,
|
const DONT_DELIM_TRAIL = 0b0000000100000000000000000000000000,
|
||||||
const ALLOW_NEG_NUMS = 0b000001000000000000000000000000000,
|
const ALLOW_NEG_NUMS = 0b0000001000000000000000000000000000,
|
||||||
const LOW_INDEX_MUL_POS = 0b000010000000000000000000000000000,
|
const LOW_INDEX_MUL_POS = 0b0000010000000000000000000000000000,
|
||||||
const DISABLE_HELP_SC = 0b000100000000000000000000000000000,
|
const DISABLE_HELP_SC = 0b0000100000000000000000000000000000,
|
||||||
const DONT_COLLAPSE_ARGS = 0b001000000000000000000000000000000,
|
const DONT_COLLAPSE_ARGS = 0b0001000000000000000000000000000000,
|
||||||
const ARGS_NEGATE_SCS = 0b010000000000000000000000000000000,
|
const ARGS_NEGATE_SCS = 0b0010000000000000000000000000000000,
|
||||||
const PROPAGATE_VALS_DOWN = 0b100000000000000000000000000000000,
|
const PROPAGATE_VALS_DOWN = 0b0100000000000000000000000000000000,
|
||||||
|
const ALLOW_MISSING_POS = 0b1000000000000000000000000000000000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +73,7 @@ impl AppFlags {
|
||||||
AllowInvalidUtf8 => UTF8_NONE,
|
AllowInvalidUtf8 => UTF8_NONE,
|
||||||
AllowLeadingHyphen => LEADING_HYPHEN,
|
AllowLeadingHyphen => LEADING_HYPHEN,
|
||||||
AllowNegativeNumbers => ALLOW_NEG_NUMS,
|
AllowNegativeNumbers => ALLOW_NEG_NUMS,
|
||||||
|
AllowMissingPositional => ALLOW_MISSING_POS,
|
||||||
ColoredHelp => COLORED_HELP,
|
ColoredHelp => COLORED_HELP,
|
||||||
ColorAlways => COLOR_ALWAYS,
|
ColorAlways => COLOR_ALWAYS,
|
||||||
ColorAuto => COLOR_AUTO,
|
ColorAuto => COLOR_AUTO,
|
||||||
|
@ -199,6 +201,39 @@ pub enum AppSettings {
|
||||||
/// [`AllowLeadingHyphen`]: ./enum.AppSettings.html#variant.AllowLeadingHyphen
|
/// [`AllowLeadingHyphen`]: ./enum.AppSettings.html#variant.AllowLeadingHyphen
|
||||||
AllowNegativeNumbers,
|
AllowNegativeNumbers,
|
||||||
|
|
||||||
|
/// Allows one to implement a CLI where the second to last positional argument is optional, but
|
||||||
|
/// the final positional argument is required. Such as `$ prog [optional] <required>` where one
|
||||||
|
/// of the two following usages is allowed:
|
||||||
|
///
|
||||||
|
/// * `$ prog [optional] <required>`
|
||||||
|
/// * `$ prog <required>`
|
||||||
|
///
|
||||||
|
/// This would otherwise not be allowed. This is useful when `[optional]` has a default value.
|
||||||
|
///
|
||||||
|
/// **Note:** In addition to using this setting, the second positional argument *must* be
|
||||||
|
/// [required]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use clap::{App, Arg, AppSettings};
|
||||||
|
/// // Assume there is an external subcommand named "subcmd"
|
||||||
|
/// let m = App::new("myprog")
|
||||||
|
/// .setting(AppSettings::AllowMissingPositional)
|
||||||
|
/// .arg(Arg::with_name("arg1")
|
||||||
|
/// .default_value("something"))
|
||||||
|
/// .arg(Arg::with_name("arg2")
|
||||||
|
/// .required(true))
|
||||||
|
/// .get_matches_from(vec![
|
||||||
|
/// "myprog", "other"
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(m.value_of("arg1"), Some("something"));
|
||||||
|
/// assert_eq!(m.value_of("arg2"), Some("other"));
|
||||||
|
/// ```
|
||||||
|
/// [required]: ./struct.Arg.html#method.required
|
||||||
|
AllowMissingPositional,
|
||||||
|
|
||||||
/// Specifies that an unexpected positional argument,
|
/// Specifies that an unexpected positional argument,
|
||||||
/// which would otherwise cause a [`ErrorKind::UnknownArgument`] error,
|
/// which would otherwise cause a [`ErrorKind::UnknownArgument`] error,
|
||||||
/// should instead be treated as a [`SubCommand`] within the [`ArgMatches`] struct.
|
/// should instead be treated as a [`SubCommand`] within the [`ArgMatches`] struct.
|
||||||
|
|
Loading…
Reference in a new issue