Skip to content

Commit

Permalink
Merge branch 'from_env' of https://github.com/bluejekyll/clap-rs into…
Browse files Browse the repository at this point in the history
… bluejekyll-from_env
  • Loading branch information
kbknapp committed Oct 7, 2017
2 parents 609a1f2 + 2fa8f83 commit 2acd9b2
Show file tree
Hide file tree
Showing 12 changed files with 844 additions and 290 deletions.
316 changes: 184 additions & 132 deletions src/app/help.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
None
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> { None }
fn longest_filter(&self) -> bool { true }
fn aliases(&self) -> Option<Vec<&'e str>> {
if let Some(ref aliases) = self.p.meta.aliases {
Expand Down
32 changes: 32 additions & 0 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1788,6 +1788,38 @@ impl<'a, 'b> Parser<'a, 'b>
}
Ok(())
}

pub fn add_env(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
macro_rules! add_val {
($_self:ident, $a:ident, $m:ident) => {
if let Some(ref val) = $a.v.env {
if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) {
$_self.add_val_to_arg($a, OsStr::new(&val.1), $m)?;

if $_self.cache.map_or(true, |name| name != $a.name()) {
arg_post_processing!($_self, $a, $m);
$_self.cache = Some($a.name());
}
} else {
$_self.add_val_to_arg($a, OsStr::new(&val.1), $m)?;

if $_self.cache.map_or(true, |name| name != $a.name()) {
arg_post_processing!($_self, $a, $m);
$_self.cache = Some($a.name());
}
}
}
};
}

for o in &self.opts {
add_val!(self, o, matcher);
}
for p in self.positionals.values() {
add_val!(self, p, matcher);
}
Ok(())
}

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

Expand Down
1 change: 1 addition & 0 deletions src/app/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
-> ClapResult<()> {
debugln!("Validator::validate;");
let mut reqs_validated = false;
self.0.add_env(matcher)?;
self.0.add_defaults(matcher)?;
if let ParseResult::Opt(a) = needs_val_of {
debugln!("Validator::validate: needs_val_of={:?}", a);
Expand Down
1 change: 1 addition & 0 deletions src/args/any_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display {
fn long_help(&self) -> Option<&'e str>;
fn default_val(&self) -> Option<&'e OsStr>;
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>;
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)>;
fn longest_filter(&self) -> bool;
fn val_terminator(&self) -> Option<&'e str>;
}
Expand Down
118 changes: 116 additions & 2 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::ffi::{OsString, OsStr};
use osstringext::OsStrExt3;
#[cfg(not(target_os="windows"))]
use std::os::unix::ffi::OsStrExt;

use std::env;

#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
Expand Down Expand Up @@ -128,6 +128,7 @@ impl<'a, 'b> Arg<'a, 'b> {
"default_value" => yaml_to_str!(a, v, default_value),
"default_value_if" => yaml_tuple3!(a, v, default_value_if),
"default_value_ifs" => yaml_tuple3!(a, v, default_value_if),
"env" => yaml_to_str!(a, v, env),
"value_names" => yaml_vec_or_str!(v, a, value_name),
"groups" => yaml_vec_or_str!(v, a, group),
"requires" => yaml_vec_or_str!(v, a, requires),
Expand Down Expand Up @@ -3014,7 +3015,7 @@ impl<'a, 'b> Arg<'a, 'b> {
/// **NOTE:** If the user *does not* use this argument at runtime [`ArgMatches::is_present`] will
/// still return `true`. If you wish to determine whether the argument was used at runtime or
/// not, consider [`ArgMatches::occurrences_of`] which will return `0` if the argument was *not*
/// used at runtmie.
/// used at runtime.
///
/// **NOTE:** This setting is perfectly compatible with [`Arg::default_value_if`] but slightly
/// different. `Arg::default_value` *only* takes affect when the user has not provided this arg
Expand Down Expand Up @@ -3311,6 +3312,119 @@ impl<'a, 'b> Arg<'a, 'b> {
self
}

/// Specifies that if the value is not passed in as an argument, that it should be retrieved
/// from the environment, if available. If it is not present in the environment, then default
/// rules will apply.
///
/// **NOTE:** If the user *does not* use this argument at runtime, [`ArgMatches::occurrences_of`]
/// will return `0` even though the [`ArgMatches::value_of`] will return the default specified.
///
/// **NOTE:** If the user *does not* use this argument at runtime [`ArgMatches::is_present`] will
/// return `true` if the variable is present in the environemnt . If you wish to determine whether
/// the argument was used at runtime or not, consider [`ArgMatches::occurrences_of`] which will
/// return `0` if the argument was *not* used at runtime.
///
/// **NOTE:** This implicitly sets [`Arg::takes_value(true)`].
///
/// **NOTE:** If [`Arg::multiple(true)`] is set then [`Arg::use_delimiter(true)`] should also be
/// set. Otherwise, only a single argument will be returned from the environment variable. The
/// default delimiter is `,` and follows all the other delimiter rules.
///
/// # Examples
///
/// In this example, we show the variable coming from the environment:
///
/// ```rust
/// # use std::env;
/// # use clap::{App, Arg};
///
/// env::set_var("MY_FLAG", "env");
///
/// let m = App::new("prog")
/// .arg(Arg::with_name("flag")
/// .long("flag")
/// .env("MY_FLAG"))
/// .get_matches_from(vec![
/// "prog"
/// ]);
///
/// assert_eq!(m.value_of("flag"), Some("env"));
/// ```
///
/// In this example, we show the variable coming from an option on the CLI:
///
/// ```rust
/// # use std::env;
/// # use clap::{App, Arg};
///
/// env::set_var("MY_FLAG", "env");
///
/// let m = App::new("prog")
/// .arg(Arg::with_name("flag")
/// .long("flag")
/// .env("MY_FLAG"))
/// .get_matches_from(vec![
/// "prog", "--flag", "opt"
/// ]);
///
/// assert_eq!(m.value_of("flag"), Some("opt"));
/// ```
///
/// In this example, we show the variable coming from the environment even with the
/// presence of a default:
///
/// ```rust
/// # use std::env;
/// # use clap::{App, Arg};
///
/// env::set_var("MY_FLAG", "env");
///
/// let m = App::new("prog")
/// .arg(Arg::with_name("flag")
/// .long("flag")
/// .env("MY_FLAG")
/// .default_value("default"))
/// .get_matches_from(vec![
/// "prog"
/// ]);
///
/// assert_eq!(m.value_of("flag"), Some("env"));
/// ```
///
/// In this example, we show the use of multiple values in a single environment variable:
///
/// ```rust
/// # use std::env;
/// # use clap::{App, Arg};
///
/// env::set_var("MY_FLAG_MULTI", "env1,env2");
///
/// let m = App::new("prog")
/// .arg(Arg::with_name("flag")
/// .long("flag")
/// .env("MY_FLAG_MULTI")
/// .multiple(true)
/// .use_delimiter(true))
/// .get_matches_from(vec![
/// "prog"
/// ]);
///
/// assert_eq!(m.values_of("flag").unwrap().collect::<Vec<_>>(), vec!["env1", "env2"]);
/// ```
pub fn env(self, name: &'a str) -> Self {
self.env_os(OsStr::new(name))
}

/// Specifies that if the value is not passed in as an argument, that it should be retrieved
/// from the environment if available in the exact same manner as [`Arg::env`] only using
/// [`OsStr`]s instead.
pub fn env_os(mut self, name: &'a OsStr) -> Self {
self.setb(ArgSettings::TakesValue);

self.v.env = env::var_os(name).map(|value| (name, value));
self
}

/// When set to `true` the help string will be displayed on the line after the argument and
/// indented once. This can be helpful for arguments with very long or complex help messages.
/// This can also be helpful for arguments with very long flag names, or many/long value names.
Expand Down
28 changes: 19 additions & 9 deletions src/args/arg_builder/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,26 @@ use std::mem;

// Internal
use Arg;
use args::{ArgSettings, Base, Switched, AnyArg, DispOrder};
use args::{AnyArg, ArgSettings, Base, DispOrder, Switched};
use map::{self, VecMap};

#[derive(Default, Clone, Debug)]
#[doc(hidden)]
pub struct FlagBuilder<'n, 'e>
where 'n: 'e
where
'n: 'e,
{
pub b: Base<'n, 'e>,
pub s: Switched<'e>,
}

impl<'n, 'e> FlagBuilder<'n, 'e> {
pub fn new(name: &'n str) -> Self { FlagBuilder { b: Base::new(name), ..Default::default() } }
pub fn new(name: &'n str) -> Self {
FlagBuilder {
b: Base::new(name),
..Default::default()
}
}
}

impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> {
Expand Down Expand Up @@ -83,10 +89,12 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
None
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> { None }
fn longest_filter(&self) -> bool { self.s.long.is_some() }
fn aliases(&self) -> Option<Vec<&'e str>> {
if let Some(ref aliases) = self.s.aliases {
let vis_aliases: Vec<_> = aliases.iter()
let vis_aliases: Vec<_> = aliases
.iter()
.filter_map(|&(n, v)| if v { Some(n) } else { None })
.collect();
if vis_aliases.is_empty() {
Expand All @@ -105,9 +113,7 @@ impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> {
}

impl<'n, 'e> PartialEq for FlagBuilder<'n, 'e> {
fn eq(&self, other: &FlagBuilder<'n, 'e>) -> bool {
self.b == other.b
}
fn eq(&self, other: &FlagBuilder<'n, 'e>) -> bool { self.b == other.b }
}

#[cfg(test)]
Expand Down Expand Up @@ -142,8 +148,12 @@ mod test {
fn flagbuilder_display_multiple_aliases() {
let mut f = FlagBuilder::new("flg");
f.s.short = Some('f');
f.s.aliases =
Some(vec![("alias_not_visible", false), ("f2", true), ("f3", true), ("f4", true)]);
f.s.aliases = Some(vec![
("alias_not_visible", false),
("f2", true),
("f3", true),
("f4", true),
]);
assert_eq!(&*format!("{}", f), "-f");
}
}
36 changes: 25 additions & 11 deletions src/args/arg_builder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ use std::ffi::{OsStr, OsString};
use std::mem;

// Internal
use args::{ArgSettings, AnyArg, Base, Switched, Valued, Arg, DispOrder};
use args::{AnyArg, Arg, ArgSettings, Base, DispOrder, Switched, Valued};
use map::{self, VecMap};

#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[derive(Default, Clone)]
pub struct OptBuilder<'n, 'e>
where 'n: 'e
where
'n: 'e,
{
pub b: Base<'n, 'e>,
pub s: Switched<'e>,
pub v: Valued<'n, 'e>,
}

impl<'n, 'e> OptBuilder<'n, 'e> {
pub fn new(name: &'n str) -> Self { OptBuilder { b: Base::new(name), ..Default::default() } }
pub fn new(name: &'n str) -> Self {
OptBuilder {
b: Base::new(name),
..Default::default()
}
}
}

impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for OptBuilder<'n, 'e> {
Expand Down Expand Up @@ -85,14 +91,16 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
write!(f, "...")?;
}
} else {
write!(f,
write!(
f,
"<{}>{}",
self.b.name,
if self.is_set(ArgSettings::Multiple) {
"..."
} else {
""
})?;
}
)?;
}

Ok(())
Expand Down Expand Up @@ -132,10 +140,14 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> {
self.v.env.as_ref().map(|&(key, ref value)| (key, value))
}
fn longest_filter(&self) -> bool { true }
fn aliases(&self) -> Option<Vec<&'e str>> {
if let Some(ref aliases) = self.s.aliases {
let vis_aliases: Vec<_> = aliases.iter()
let vis_aliases: Vec<_> = aliases
.iter()
.filter_map(|&(n, v)| if v { Some(n) } else { None })
.collect();
if vis_aliases.is_empty() {
Expand All @@ -154,9 +166,7 @@ impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> {
}

impl<'n, 'e> PartialEq for OptBuilder<'n, 'e> {
fn eq(&self, other: &OptBuilder<'n, 'e>) -> bool {
self.b == other.b
}
fn eq(&self, other: &OptBuilder<'n, 'e>) -> bool { self.b == other.b }
}

#[cfg(test)]
Expand Down Expand Up @@ -214,8 +224,12 @@ mod test {
fn optbuilder_display_multiple_aliases() {
let mut o = OptBuilder::new("opt");
o.s.long = Some("option");
o.s.aliases =
Some(vec![("als_not_visible", false), ("als2", true), ("als3", true), ("als4", true)]);
o.s.aliases = Some(vec![
("als_not_visible", false),
("als2", true),
("als3", true),
("als4", true),
]);
assert_eq!(&*format!("{}", o), "--option <opt>");
}
}
Loading

0 comments on commit 2acd9b2

Please sign in to comment.