setting(AllowMissingPositional): allows one to implement $ prog [optional] <required>

Closes #636
This commit is contained in:
Kevin K 2017-01-03 21:56:37 -05:00
parent ce083919be
commit 6edde30b8e
No known key found for this signature in database
GPG key ID: 17218E4B3692F01A
2 changed files with 109 additions and 50 deletions

View file

@ -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 {

View file

@ -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.