Skip to content

Commit

Permalink
feat(arg): allow other types besides Vec for multiple value settings
Browse files Browse the repository at this point in the history
Breaking Change

Instead of requiring a Vec<&str> for various Arg::*_all() and
Arg::possible_values() methods this
commit now requires a generic IntoIterator<Item=AsRef<str>> which allows
things such as constant arrays. This change requires that any
Arg::*_all() methods be changed from vec!["val", "val"] -> let vals =
["val", "val"]; some_arg.possible_values(&vals) (or vals.iter()).

Closes #87
  • Loading branch information
kbknapp committed Apr 29, 2015
1 parent 9e6af52 commit 0cc2f69
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 34 deletions.
7 changes: 5 additions & 2 deletions clap-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ extern crate clap;

use clap::{App, Arg, SubCommand};


fn main() {
let args = "-f --flag... 'tests flags'
-o --option=[opt]... 'tests options'
[positional] 'tests positionals'";
let opt3_vals = ["fast", "slow"];
let pos3_vals = ["vi", "emacs"];
let matches = App::new("claptests")
// Test version from Cargo.toml
.version(&crate_version!()[..])
Expand All @@ -17,8 +20,8 @@ fn main() {
Arg::from_usage("[flag2] -F 'tests flags with exclusions'").mutually_excludes("flag").requires("option2"),
Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").mutually_excludes("option").requires("positional2"),
Arg::from_usage("[positional2] 'tests positionals with exclusions'"),
Arg::from_usage("-O [option3] 'tests options with specific value sets'").possible_values(vec!["fast", "slow"]),
Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(vec!["vi", "emacs"])
Arg::from_usage("-O [option3] 'tests options with specific value sets'").possible_values(&opt3_vals),
Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(&pos3_vals)
])
.subcommand(SubCommand::new("subcmd")
.about("tests subcommands")
Expand Down
3 changes: 2 additions & 1 deletion examples/11_OnlySpecificValues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ fn main() {
//
// For this example, assume you want one positional argument of either "fast" or "slow"
// i.e. the only possible ways to run the program are "myprog fast" or "myprog slow"
let mode_vals = ["fast", "slow"];
let matches = App::new("myapp").about("does awesome things")
.arg(Arg::with_name("MODE")
.help("What mode to run the program in")
.index(1)
.possible_values(vec!["fast", "slow"])
.possible_values(&mode_vals)
.required(true))
.get_matches();

Expand Down
3 changes: 2 additions & 1 deletion examples/13a_EnumValuesAutomatic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ arg_enum!{

fn main() {
// Create the application like normal
let enum_vals = ["fast", "slow"];
let m = App::new("myapp")
// Use a single positional argument that is required
.arg(Arg::from_usage("<type> 'The Foo to use'")
// You can define a list of possible values if you want the values to be
// displayed in the help information. Whether you use possible_values() or
// not, the valid values will ALWAYS be displayed on a failed parse.
.possible_values(vec!["Bar", "Baz", "Qux"]))
.possible_values(&enum_vals))
// For the second positional, lets not use possible_values() just to show the difference
.arg_from_usage("<type2> 'The Oof to use'")
.get_matches();
Expand Down
3 changes: 2 additions & 1 deletion examples/13b_EnumValuesManual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ impl FromStr for Vals {

fn main() {
// Create the application like normal
let enum_vals = ["Foo", "Bar", "Baz", "Qux"];
let m = App::new("myapp")
// Use a single positional argument that is required
.arg(Arg::from_usage("<type> 'The type to use'")
// Define the list of possible values
.possible_values(vec!["Foo", "Bar", "Baz", "Qux"]))
.possible_values(&enum_vals))
.get_matches();

let t = value_t_or_exit!(m.value_of("type"), Vals);
Expand Down
64 changes: 36 additions & 28 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::iter::IntoIterator;

use usageparser::{UsageParser, UsageToken};

/// The abstract representation of a command line argument used by the consumer of the library.
Expand Down Expand Up @@ -379,7 +381,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
if let Some(ref mut vec) = self.blacklist {
vec.push(name);
} else {
self.blacklist = Some(vec![name]);
let v = vec![name];
self.blacklist = Some(v);
}
self
}
Expand All @@ -398,17 +401,18 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
///
/// ```no_run
/// # use clap::{App, Arg};
/// let conf_excludes = ["debug", "input"];
/// # let myprog = App::new("myprog").arg(Arg::with_name("conifg")
/// .mutually_excludes_all(
/// vec!["debug", "input"])
/// .mutually_excludes_all(&conf_excludes)
/// # ).get_matches();
pub fn mutually_excludes_all(mut self, names: Vec<&'r str>) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
pub fn mutually_excludes_all<T, I>(mut self, names: I)
-> Arg<'n, 'l, 'h, 'g, 'p, 'r>
where T: AsRef<str> + 'r,
I: IntoIterator<Item=&'r T> {
if let Some(ref mut vec) = self.blacklist {
for n in names {
vec.push(n);
}
names.into_iter().map(|s| vec.push(s.as_ref())).collect::<Vec<_>>();
} else {
self.blacklist = Some(names);
self.blacklist = Some(names.into_iter().map(|s| s.as_ref()).collect::<Vec<_>>());
}
self
}
Expand Down Expand Up @@ -449,17 +453,18 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
///
/// ```no_run
/// # use clap::{App, Arg};
/// let config_conflicts = ["debug", "input"];
/// # let myprog = App::new("myprog").arg(Arg::with_name("conifg")
/// .conflicts_with_all(
/// vec!["debug", "input"])
/// .conflicts_with_all(&config_conflicts)
/// # ).get_matches();
pub fn conflicts_with_all(mut self, names: Vec<&'r str>) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
pub fn conflicts_with_all<T, I>(mut self, names: I)
-> Arg<'n, 'l, 'h, 'g, 'p, 'r>
where T: AsRef<str> + 'r,
I: IntoIterator<Item=&'r T> {
if let Some(ref mut vec) = self.blacklist {
for n in names {
vec.push(n);
}
names.into_iter().map(|s| vec.push(s.as_ref())).collect::<Vec<_>>();
} else {
self.blacklist = Some(names);
self.blacklist = Some(names.into_iter().map(|s| s.as_ref()).collect::<Vec<_>>());
}
self
}
Expand Down Expand Up @@ -497,17 +502,18 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
///
/// ```no_run
/// # use clap::{App, Arg};
/// let config_reqs = ["debug", "input"];
/// # let myprog = App::new("myprog").arg(Arg::with_name("conifg")
/// .requires_all(
/// vec!["debug", "input"])
/// .requires_all(&config_reqs)
/// # ).get_matches();
pub fn requires_all(mut self, names: Vec<&'r str>) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
pub fn requires_all<T, I>(mut self, names: I)
-> Arg<'n, 'l, 'h, 'g, 'p, 'r>
where T: AsRef<str> + 'r,
I: IntoIterator<Item=&'r T> {
if let Some(ref mut vec) = self.requires {
for n in names {
vec.push(n);
}
names.into_iter().map(|s| vec.push(s.as_ref())).collect::<Vec<_>>();
} else {
self.requires = Some(names);
self.requires = Some(names.into_iter().map(|s| s.as_ref()).collect::<Vec<_>>());
}
self
}
Expand Down Expand Up @@ -587,18 +593,20 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
///
/// ```no_run
/// # use clap::{App, Arg};
/// let mode_vals = ["fast", "slow"];
/// # let matches = App::new("myprog")
/// # .arg(
/// # Arg::with_name("debug").index(1)
/// .possible_values(vec!["fast", "slow"])
/// .possible_values(&mode_vals)
/// # ).get_matches();
pub fn possible_values(mut self, names: Vec<&'p str>) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
pub fn possible_values<T, I>(mut self, names: I)
-> Arg<'n, 'l, 'h, 'g, 'p, 'r>
where T: AsRef<str> + 'p,
I: IntoIterator<Item=&'p T> {
if let Some(ref mut vec) = self.possible_vals {
for n in names {
vec.push(n);
}
names.into_iter().map(|s| vec.push(s.as_ref())).collect::<Vec<_>>();
} else {
self.possible_vals = Some(names);
self.possible_vals = Some(names.into_iter().map(|s| s.as_ref()).collect::<Vec<_>>());
}
self
}
Expand Down
3 changes: 2 additions & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,10 @@ macro_rules! value_t_or_exit {
/// // Foo enum can now be used via Foo::Bar, or Foo::Baz, etc
/// // and implements std::str::FromStr to use with the value_t! macros
/// fn main() {
/// let enum_vals = ["Bar", "Baz", "Qux"];
/// let m = App::new("app")
/// .arg(Arg::from_usage("<foo> 'the foo'")
/// .possible_values(vec!["Bar", "Baz", "Qux"]))
/// .possible_values(&enum_vals))
/// .get_matches();
/// let f = value_t_or_exit!(m.value_of("foo"), Foo);
///
Expand Down

0 comments on commit 0cc2f69

Please sign in to comment.