Skip to content

Commit

Permalink
WIP: add from_env option
Browse files Browse the repository at this point in the history
  • Loading branch information
benj-fry-sf committed Sep 29, 2017
1 parent 6e94899 commit 306807f
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 157 deletions.
1 change: 1 addition & 0 deletions src/app/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ impl<'a> Help<'a> {
Format::None(pv.to_string_lossy())
}));
}
// FIXME: add [env::{}: {}] here
}
if let Some(ref aliases) = a.aliases() {
debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
Expand Down
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<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
None
}
fn from_env(&self) -> Option<&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 @@ -1790,6 +1790,38 @@ impl<'a, 'b> Parser<'a, 'b>
}
Ok(())
}

pub fn add_from_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.from_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), $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), $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_from_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 @@ -35,6 +35,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<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>;
fn from_env(&self) -> Option<&OsString>;
fn longest_filter(&self) -> bool;
fn val_terminator(&self) -> Option<&'e str>;
}
Expand Down
13 changes: 12 additions & 1 deletion 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),
"from_env" => yaml_to_str!(a, v, from_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 @@ -3311,6 +3312,16 @@ 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.
pub fn from_env<K: AsRef<OsStr>>(mut self, name: K) -> Self {
self.setb(ArgSettings::TakesValue);

self.v.from_env = env::var_os(name);
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 @@ -11,19 +11,25 @@ use vec_map::{self, VecMap};

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

#[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 @@ -85,10 +91,12 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
None
}
fn from_env(&self) -> Option<&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 @@ -107,9 +115,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 @@ -144,8 +150,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");
}
}
34 changes: 23 additions & 11 deletions src/args/arg_builder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,27 @@ use std::mem;
use vec_map::{self, VecMap};

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

#[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 @@ -87,14 +93,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 @@ -134,10 +142,12 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
}
fn from_env(&self) -> Option<&OsString> { self.v.from_env.as_ref() }
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 @@ -156,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 @@ -216,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>");
}
}
34 changes: 22 additions & 12 deletions src/args/arg_builder/positional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ use vec_map::{self, VecMap};

// Internal
use Arg;
use args::{ArgSettings, Base, Valued, AnyArg, DispOrder};
use args::{AnyArg, ArgSettings, Base, DispOrder, Valued};
use INTERNAL_ERROR_MSG;

#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[derive(Clone, Default)]
pub struct PosBuilder<'n, 'e>
where 'n: 'e
where
'n: 'e,
{
pub b: Base<'n, 'e>,
pub v: Valued<'n, 'e>,
Expand All @@ -41,15 +42,17 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
index: idx,
};
if a.v.max_vals.is_some() || a.v.min_vals.is_some() ||
(a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1) {
(a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1)
{
pb.b.settings.set(ArgSettings::Multiple);
}
pb
}

pub fn from_arg(mut a: Arg<'n, 'e>, idx: u64) -> Self {
if a.v.max_vals.is_some() || a.v.min_vals.is_some() ||
(a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1) {
(a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1)
{
a.b.settings.set(ArgSettings::Multiple);
}
PosBuilder {
Expand All @@ -76,11 +79,13 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
if let Some(ref names) = self.v.val_names {
debugln!("PosBuilder:name_no_brackets: val_names={:#?}", names);
if names.len() > 1 {
Cow::Owned(names
.values()
.map(|n| format!("<{}>", n))
.collect::<Vec<_>>()
.join(" "))
Cow::Owned(
names
.values()
.map(|n| format!("<{}>", n))
.collect::<Vec<_>>()
.join(" "),
)
} else {
Cow::Borrowed(names.values().next().expect(INTERNAL_ERROR_MSG))
}
Expand All @@ -94,17 +99,21 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
impl<'n, 'e> Display for PosBuilder<'n, 'e> {
fn fmt(&self, f: &mut Formatter) -> Result {
if let Some(ref names) = self.v.val_names {
write!(f,
write!(
f,
"{}",
names
.values()
.map(|n| format!("<{}>", n))
.collect::<Vec<_>>()
.join(" "))?;
.join(" ")
)?;
} else {
write!(f, "<{}>", self.b.name)?;
}
if self.b.settings.is_set(ArgSettings::Multiple) && (self.v.val_names.is_none() || self.v.val_names.as_ref().unwrap().len() == 1) {
if self.b.settings.is_set(ArgSettings::Multiple) &&
(self.v.val_names.is_none() || self.v.val_names.as_ref().unwrap().len() == 1)
{
write!(f, "...")?;
}

Expand Down Expand Up @@ -145,6 +154,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
}
fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val }
fn from_env(&self) -> Option<&OsString> { self.v.from_env.as_ref() }
fn longest_filter(&self) -> bool { true }
fn aliases(&self) -> Option<Vec<&'e str>> { None }
}
Expand Down
5 changes: 4 additions & 1 deletion src/args/arg_builder/valued.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use Arg;
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct Valued<'a, 'b>
where 'a: 'b
where
'a: 'b,
{
pub possible_vals: Option<Vec<&'b str>>,
pub val_names: Option<VecMap<&'b str>>,
Expand All @@ -20,6 +21,7 @@ pub struct Valued<'a, 'b>
pub val_delim: Option<char>,
pub default_val: Option<&'b OsStr>,
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b OsStr>, &'b OsStr)>>,
pub from_env: Option<OsString>,
pub terminator: Option<&'b str>,
}

Expand All @@ -36,6 +38,7 @@ impl<'n, 'e> Default for Valued<'n, 'e> {
val_delim: None,
default_val: None,
default_vals_ifs: None,
from_env: None,
terminator: None,
}
}
Expand Down
Loading

0 comments on commit 306807f

Please sign in to comment.