Skip to content

Commit

Permalink
setting(AllowMissingPositional): allows one to implement `$ prog [opt…
Browse files Browse the repository at this point in the history
…ional] <required>`

Closes #636
  • Loading branch information
kbknapp committed Jan 4, 2017
1 parent ce08391 commit 1110fdc
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 50 deletions.
58 changes: 41 additions & 17 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,6 @@ impl<'a, 'b> Parser<'a, 'b>
a.b.settings.is_set(ArgSettings::Multiple) &&
(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!({
let mut it = self.positionals.values().rev();
Expand Down Expand Up @@ -581,17 +577,42 @@ impl<'a, 'b> Parser<'a, 'b>

// If it's required we also need to ensure all previous positionals are
// required too
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;
if self.is_set(AppSettings::AllowMissingPositional) {
let mut found = false;
let mut foundx2 = false;
for p in self.positionals.values().rev() {
if foundx2 && !p.b.settings.is_set(ArgSettings::Required) {
// [arg1] <arg2> is Ok
// [arg1] <arg2> <arg3> Is not
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 by two or more: {:?} index {}",
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;
}
}
}
}
Expand Down Expand Up @@ -861,12 +882,15 @@ impl<'a, 'b> Parser<'a, 'b>
let low_index_mults = self.is_set(AppSettings::LowIndexMultiplePositional) &&
!self.positionals.is_empty() &&
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: Positional counter...{}", pos_counter);
if low_index_mults {
if low_index_mults || missing_pos {
if let Some(na) = it.peek() {
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) {
Some(p.b.name)
} else {
Expand Down
101 changes: 68 additions & 33 deletions src/app/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,40 @@ use std::ops::BitOr;

bitflags! {
flags Flags: u64 {
const SC_NEGATE_REQS = 0b000000000000000000000000000000001,
const SC_REQUIRED = 0b000000000000000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b000000000000000000000000000000100,
const GLOBAL_VERSION = 0b000000000000000000000000000001000,
const VERSIONLESS_SC = 0b000000000000000000000000000010000,
const UNIFIED_HELP = 0b000000000000000000000000000100000,
const WAIT_ON_ERROR = 0b000000000000000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b000000000000000000000000010000000,
const NEEDS_LONG_HELP = 0b000000000000000000000000100000000,
const NEEDS_LONG_VERSION = 0b000000000000000000000001000000000,
const NEEDS_SC_HELP = 0b000000000000000000000010000000000,
const DISABLE_VERSION = 0b000000000000000000000100000000000,
const HIDDEN = 0b000000000000000000001000000000000,
const TRAILING_VARARG = 0b000000000000000000010000000000000,
const NO_BIN_NAME = 0b000000000000000000100000000000000,
const ALLOW_UNK_SC = 0b000000000000000001000000000000000,
const UTF8_STRICT = 0b000000000000000010000000000000000,
const UTF8_NONE = 0b000000000000000100000000000000000,
const LEADING_HYPHEN = 0b000000000000001000000000000000000,
const NO_POS_VALUES = 0b000000000000010000000000000000000,
const NEXT_LINE_HELP = 0b000000000000100000000000000000000,
const DERIVE_DISP_ORDER = 0b000000000001000000000000000000000,
const COLORED_HELP = 0b000000000010000000000000000000000,
const COLOR_ALWAYS = 0b000000000100000000000000000000000,
const COLOR_AUTO = 0b000000001000000000000000000000000,
const COLOR_NEVER = 0b000000010000000000000000000000000,
const DONT_DELIM_TRAIL = 0b000000100000000000000000000000000,
const ALLOW_NEG_NUMS = 0b000001000000000000000000000000000,
const LOW_INDEX_MUL_POS = 0b000010000000000000000000000000000,
const DISABLE_HELP_SC = 0b000100000000000000000000000000000,
const DONT_COLLAPSE_ARGS = 0b001000000000000000000000000000000,
const ARGS_NEGATE_SCS = 0b010000000000000000000000000000000,
const PROPAGATE_VALS_DOWN = 0b100000000000000000000000000000000,
const SC_NEGATE_REQS = 0b0000000000000000000000000000000001,
const SC_REQUIRED = 0b0000000000000000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b0000000000000000000000000000000100,
const GLOBAL_VERSION = 0b0000000000000000000000000000001000,
const VERSIONLESS_SC = 0b0000000000000000000000000000010000,
const UNIFIED_HELP = 0b0000000000000000000000000000100000,
const WAIT_ON_ERROR = 0b0000000000000000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b0000000000000000000000000010000000,
const NEEDS_LONG_HELP = 0b0000000000000000000000000100000000,
const NEEDS_LONG_VERSION = 0b0000000000000000000000001000000000,
const NEEDS_SC_HELP = 0b0000000000000000000000010000000000,
const DISABLE_VERSION = 0b0000000000000000000000100000000000,
const HIDDEN = 0b0000000000000000000001000000000000,
const TRAILING_VARARG = 0b0000000000000000000010000000000000,
const NO_BIN_NAME = 0b0000000000000000000100000000000000,
const ALLOW_UNK_SC = 0b0000000000000000001000000000000000,
const UTF8_STRICT = 0b0000000000000000010000000000000000,
const UTF8_NONE = 0b0000000000000000100000000000000000,
const LEADING_HYPHEN = 0b0000000000000001000000000000000000,
const NO_POS_VALUES = 0b0000000000000010000000000000000000,
const NEXT_LINE_HELP = 0b0000000000000100000000000000000000,
const DERIVE_DISP_ORDER = 0b0000000000001000000000000000000000,
const COLORED_HELP = 0b0000000000010000000000000000000000,
const COLOR_ALWAYS = 0b0000000000100000000000000000000000,
const COLOR_AUTO = 0b0000000001000000000000000000000000,
const COLOR_NEVER = 0b0000000010000000000000000000000000,
const DONT_DELIM_TRAIL = 0b0000000100000000000000000000000000,
const ALLOW_NEG_NUMS = 0b0000001000000000000000000000000000,
const LOW_INDEX_MUL_POS = 0b0000010000000000000000000000000000,
const DISABLE_HELP_SC = 0b0000100000000000000000000000000000,
const DONT_COLLAPSE_ARGS = 0b0001000000000000000000000000000000,
const ARGS_NEGATE_SCS = 0b0010000000000000000000000000000000,
const PROPAGATE_VALS_DOWN = 0b0100000000000000000000000000000000,
const ALLOW_MISSING_POS = 0b1000000000000000000000000000000000,
}
}

Expand Down Expand Up @@ -72,6 +73,7 @@ impl AppFlags {
AllowInvalidUtf8 => UTF8_NONE,
AllowLeadingHyphen => LEADING_HYPHEN,
AllowNegativeNumbers => ALLOW_NEG_NUMS,
AllowMissingPositional => ALLOW_MISSING_POS,
ColoredHelp => COLORED_HELP,
ColorAlways => COLOR_ALWAYS,
ColorAuto => COLOR_AUTO,
Expand Down Expand Up @@ -199,6 +201,39 @@ pub enum AppSettings {
/// [`AllowLeadingHyphen`]: ./enum.AppSettings.html#variant.AllowLeadingHyphen
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, 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,
/// which would otherwise cause a [`ErrorKind::UnknownArgument`] error,
/// should instead be treated as a [`SubCommand`] within the [`ArgMatches`] struct.
Expand Down

0 comments on commit 1110fdc

Please sign in to comment.