Skip to content

Commit

Permalink
imp(AllowMissingPositional): improves the ability of AllowMissingPosi…
Browse files Browse the repository at this point in the history
…tional to allow 'skipping' to the last positional arg with '--'
  • Loading branch information
kbknapp committed Mar 6, 2018
1 parent 88c8eca commit df20e6e
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 48 deletions.
63 changes: 19 additions & 44 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,7 @@ where
}
}

pub fn required(&self) -> Iter<&str> {
self.required.iter()
}
pub fn required(&self) -> Iter<&str> { self.required.iter() }

#[cfg_attr(feature = "lints", allow(needless_borrow))]
#[inline]
Expand All @@ -455,24 +453,16 @@ where
}

#[inline]
pub fn has_opts(&self) -> bool {
!self.opts.is_empty()
}
pub fn has_opts(&self) -> bool { !self.opts.is_empty() }

#[inline]
pub fn has_flags(&self) -> bool {
!self.flags.is_empty()
}
pub fn has_flags(&self) -> bool { !self.flags.is_empty() }

#[inline]
pub fn has_positionals(&self) -> bool {
!self.positionals.is_empty()
}
pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() }

#[inline]
pub fn has_subcommands(&self) -> bool {
!self.subcommands.is_empty()
}
pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() }

#[inline]
pub fn has_visible_opts(&self) -> bool {
Expand Down Expand Up @@ -510,19 +500,13 @@ where
}

#[inline]
pub fn is_set(&self, s: AS) -> bool {
self.settings.is_set(s)
}
pub fn is_set(&self, s: AS) -> bool { self.settings.is_set(s) }

#[inline]
pub fn set(&mut self, s: AS) {
self.settings.set(s)
}
pub fn set(&mut self, s: AS) { self.settings.set(s) }

#[inline]
pub fn unset(&mut self, s: AS) {
self.settings.unset(s)
}
pub fn unset(&mut self, s: AS) { self.settings.unset(s) }

#[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))]
pub fn verify_positionals(&mut self) -> bool {
Expand Down Expand Up @@ -1010,7 +994,8 @@ where
let low_index_mults = self.is_set(AS::LowIndexMultiplePositional)
&& pos_counter == (self.positionals.len() - 1);
let missing_pos = self.is_set(AS::AllowMissingPositional)
&& pos_counter == (self.positionals.len() - 1);
&& (pos_counter == (self.positionals.len() - 1)
&& !self.is_set(AS::TrailingValues));
debugln!(
"Parser::get_matches_with: Positional counter...{}",
pos_counter
Expand Down Expand Up @@ -1043,7 +1028,9 @@ where
debugln!("Parser::get_matches_with: Bumping the positional counter...");
pos_counter += 1;
}
} else if self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues) {
} else if (self.is_set(AS::AllowMissingPositional) && self.is_set(AS::TrailingValues))
|| (self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues))
{
// Came to -- and one postional has .last(true) set, so we go immediately
// to the last (highest index) positional
debugln!("Parser::get_matches_with: .last(true) and --, setting last pos");
Expand Down Expand Up @@ -2047,21 +2034,13 @@ where
Ok(())
}

pub fn flags(&self) -> Iter<FlagBuilder<'a, 'b>> {
self.flags.iter()
}
pub fn flags(&self) -> Iter<FlagBuilder<'a, 'b>> { self.flags.iter() }

pub fn opts(&self) -> Iter<OptBuilder<'a, 'b>> {
self.opts.iter()
}
pub fn opts(&self) -> Iter<OptBuilder<'a, 'b>> { self.opts.iter() }

pub fn positionals(&self) -> map::Values<PosBuilder<'a, 'b>> {
self.positionals.values()
}
pub fn positionals(&self) -> map::Values<PosBuilder<'a, 'b>> { self.positionals.values() }

pub fn subcommands(&self) -> Iter<App> {
self.subcommands.iter()
}
pub fn subcommands(&self) -> Iter<App> { self.subcommands.iter() }

// Should we color the output? None=determined by output location, true=yes, false=no
#[doc(hidden)]
Expand Down Expand Up @@ -2145,12 +2124,8 @@ where
}

#[inline]
fn contains_long(&self, l: &str) -> bool {
longs!(self).any(|al| al == &l)
}
fn contains_long(&self, l: &str) -> bool { longs!(self).any(|al| al == &l) }

#[inline]
fn contains_short(&self, s: char) -> bool {
shorts!(self).any(|arg_s| arg_s == &s)
}
fn contains_short(&self, s: char) -> bool { shorts!(self).any(|arg_s| arg_s == &s) }
}
82 changes: 78 additions & 4 deletions src/app/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ 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
/// Allows one to implement two styles of CLIs where positionals can be used out of order.
///
/// The first example is 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:
///
Expand All @@ -230,11 +232,46 @@ pub enum AppSettings {
///
/// 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]
/// **Note:** when using this style of "missing positionals" the final positional *must* be
/// [required] if `--` will not be used to skip to the final positional argument.
///
/// **Note:** This style also only allows a single positional argument to be "skipped" without
/// the use of `--`. To skip more than one, see the second example.
///
/// The second example is when one wants to skip multiple optional positional arguments, and use
/// of the `--` operator is OK (but not required if all arguments will be specified anyways).
///
/// For example, imagine a CLI which has three positional arguments `[foo] [bar] [baz]...` where
/// `baz` accepts multiple values (similar to man `ARGS...` style training arguments).
///
/// With this setting the following invocations are posisble:
///
/// * `$ prog foo bar baz1 baz2 baz3`
/// * `$ prog foo -- baz1 baz2 baz3`
/// * `$ prog -- baz1 baz2 baz3`
///
/// # Examples
///
/// Style number one from above:
///
/// ```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"))
/// .arg(Arg::with_name("arg2")
/// .required(true))
/// .get_matches_from(vec![
/// "prog", "other"
/// ]);
///
/// assert_eq!(m.value_of("arg1"), None);
/// assert_eq!(m.value_of("arg2"), Some("other"));
/// ```
///
/// Now the same example, but using a default value for the first optional positional argument
///
/// ```rust
/// # use clap::{App, Arg, AppSettings};
/// // Assume there is an external subcommand named "subcmd"
Expand All @@ -245,12 +282,49 @@ pub enum AppSettings {
/// .arg(Arg::with_name("arg2")
/// .required(true))
/// .get_matches_from(vec![
/// "myprog", "other"
/// "prog", "other"
/// ]);
///
/// assert_eq!(m.value_of("arg1"), Some("something"));
/// assert_eq!(m.value_of("arg2"), Some("other"));
/// ```
/// Style number two from above:
///
/// ```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("foo"))
/// .arg(Arg::with_name("bar"))
/// .arg(Arg::with_name("baz").multiple(true))
/// .get_matches_from(vec![
/// "prog", "foo", "bar", "baz1", "baz2", "baz3"
/// ]);
///
/// assert_eq!(m.value_of("foo"), Some("foo"));
/// assert_eq!(m.value_of("bar"), Some("bar"));
/// assert_eq!(m.values_of("baz").unwrap().collect::<Vec<_>>(), &["baz1", "baz2", "baz3"]);
/// ```
///
/// Now nofice if we don't specifiy `foo` or `baz` but use the `--` operator.
///
/// ```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("foo"))
/// .arg(Arg::with_name("bar"))
/// .arg(Arg::with_name("baz").multiple(true))
/// .get_matches_from(vec![
/// "prog", "--", "baz1", "baz2", "baz3"
/// ]);
///
/// assert_eq!(m.value_of("foo"), None);
/// assert_eq!(m.value_of("bar"), None);
/// assert_eq!(m.values_of("baz").unwrap().collect::<Vec<_>>(), &["baz1", "baz2", "baz3"]);
/// ```
/// [required]: ./struct.Arg.html#method.required
AllowMissingPositional,

Expand Down

0 comments on commit df20e6e

Please sign in to comment.