diff --git a/src/app/help.rs b/src/app/help.rs index a5ccc830..f09d0088 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -10,22 +10,19 @@ use app::{App, AppSettings}; use app::parser::Parser; use args::{AnyArg, ArgSettings, DispOrder}; use errors::{Error, Result as ClapResult}; -use fmt::{Format, Colorizer, ColorizerOption}; +use fmt::{Colorizer, ColorizerOption, Format}; use app::usage; use map::VecMap; // Third Party use unicode_width::UnicodeWidthStr; +#[cfg(feature = "wrap_help")] +use term_size; use textwrap; -#[cfg(feature = "wrap_help")] -fn term_width() -> usize { - textwrap::termwidth() -} - #[cfg(not(feature = "wrap_help"))] -fn term_width() -> usize { - 120 +mod term_size { + pub fn dimensions() -> Option<(usize, usize)> { None } } fn str_width(s: &str) -> usize { UnicodeWidthStr::width(s) } @@ -34,13 +31,18 @@ const TAB: &'static str = " "; // These are just convenient traits to make the code easier to read. trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {} -impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T where T: AnyArg<'b, 'c> + Display {} +impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T +where + T: AnyArg<'b, 'c> + Display, +{ +} trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder { fn as_base(&self) -> &ArgWithDisplay<'b, 'c>; } impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T - where T: ArgWithDisplay<'b, 'c> + DispOrder +where + T: ArgWithDisplay<'b, 'c> + DispOrder, { fn as_base(&self) -> &ArgWithDisplay<'b, 'c> { self } } @@ -87,29 +89,34 @@ pub struct Help<'a> { impl<'a> Help<'a> { /// Create a new `Help` instance. #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] - pub fn new(w: &'a mut Write, - next_line_help: bool, - hide_pv: bool, - color: bool, - cizer: Colorizer, - term_w: Option, - max_w: Option, - use_long: bool) - -> Self { + pub fn new( + w: &'a mut Write, + next_line_help: bool, + hide_pv: bool, + color: bool, + cizer: Colorizer, + term_w: Option, + max_w: Option, + use_long: bool, + ) -> Self { debugln!("Help::new;"); Help { writer: w, next_line_help: next_line_help, hide_pv: hide_pv, term_w: match term_w { - Some(width) => if width == 0 { usize::MAX } else { width }, - None => { - cmp::min(term_width(), - match max_w { - None | Some(0) => usize::MAX, - Some(mw) => mw, - }) - } + Some(width) => if width == 0 { + usize::MAX + } else { + width + }, + None => cmp::min( + term_size::dimensions().map_or(120, |(w, _)| w), + match max_w { + None | Some(0) => usize::MAX, + Some(mw) => mw, + }, + ), }, color: color, cizer: cizer, @@ -142,7 +149,12 @@ impl<'a> Help<'a> { } #[doc(hidden)] - pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool, use_long: bool) -> ClapResult<()> { + pub fn _write_parser_help( + w: &'a mut Write, + parser: &Parser, + stderr: bool, + use_long: bool, + ) -> ClapResult<()> { debugln!("Help::write_parser_help;"); let nlh = parser.is_set(AppSettings::NextLineHelp); let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp); @@ -151,15 +163,16 @@ impl<'a> Help<'a> { use_stderr: stderr, when: parser.color(), }); - Self::new(w, - nlh, - hide_v, - color, - cizer, - parser.meta.term_w, - parser.meta.max_w, - use_long) - .write_help(parser) + Self::new( + w, + nlh, + hide_v, + color, + cizer, + parser.meta.term_w, + parser.meta.max_w, + use_long, + ).write_help(parser) } /// Writes the parser help to the wrapped stream. @@ -180,16 +193,16 @@ impl<'a> Help<'a> { impl<'a> Help<'a> { /// Writes help for each argument in the order they were declared to the wrapped stream. fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()> - where I: Iterator> + where + I: Iterator>, { debugln!("Help::write_args_unsorted;"); // The shortest an arg can legally be is 2 (i.e. '-x') self.longest = 2; let mut arg_v = Vec::with_capacity(10); for arg in args.filter(|arg| { - !(arg.is_set(ArgSettings::Hidden)) || - arg.is_set(ArgSettings::NextLineHelp) - }) { + !(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp) + }) { if arg.longest_filter() { self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str())); } @@ -209,7 +222,8 @@ impl<'a> Help<'a> { /// Sorts arguments by length and display order and write their help to the wrapped stream. fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()> - where I: Iterator> + where + I: Iterator>, { debugln!("Help::write_args;"); // The shortest an arg can legally be is 2 (i.e. '-x') @@ -338,9 +352,9 @@ impl<'a> Help<'a> { let h_w = str_width(h) + str_width(&*spec_vals); let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp); let taken = self.longest + 12; - self.force_next_line = !nlh && self.term_w >= taken && - (taken as f32 / self.term_w as f32) > 0.40 && - h_w > (self.term_w - taken); + self.force_next_line = !nlh && self.term_w >= taken + && (taken as f32 / self.term_w as f32) > 0.40 + && h_w > (self.term_w - taken); debug!("Help::val: Has switch..."); if arg.has_switch() { @@ -348,10 +362,12 @@ impl<'a> Help<'a> { debugln!("Help::val: force_next_line...{:?}", self.force_next_line); debugln!("Help::val: nlh...{:?}", nlh); debugln!("Help::val: taken...{}", taken); - debugln!("Help::val: help_width > (width - taken)...{} > ({} - {})", - h_w, - self.term_w, - taken); + debugln!( + "Help::val: help_width > (width - taken)...{} > ({} - {})", + h_w, + self.term_w, + taken + ); debugln!("Help::val: longest...{}", self.longest); debug!("Help::val: next_line..."); if !(nlh || self.force_next_line) { @@ -375,7 +391,10 @@ impl<'a> Help<'a> { } } else if !(nlh || self.force_next_line) { sdebugln!("No, and not next_line"); - write_nspaces!(self.writer, self.longest + 4 - (str_width(arg.to_string().as_str()))); + write_nspaces!( + self.writer, + self.longest + 4 - (str_width(arg.to_string().as_str())) + ); } else { sdebugln!("No"); } @@ -386,19 +405,25 @@ impl<'a> Help<'a> { debugln!("Help::write_before_after_help;"); let mut help = String::from(h); // determine if our help fits or needs to wrap - debugln!("Help::write_before_after_help: Term width...{}", - self.term_w); + debugln!( + "Help::write_before_after_help: Term width...{}", + self.term_w + ); let too_long = str_width(h) >= self.term_w; debug!("Help::write_before_after_help: Too long..."); if too_long || h.contains("{n}") { sdebugln!("Yes"); debugln!("Help::write_before_after_help: help: {}", help); - debugln!("Help::write_before_after_help: help width: {}", - str_width(&*help)); + debugln!( + "Help::write_before_after_help: help width: {}", + str_width(&*help) + ); // Determine how many newlines we need to insert - debugln!("Help::write_before_after_help: Usable space: {}", - self.term_w); + debugln!( + "Help::write_before_after_help: Usable space: {}", + self.term_w + ); help = wrap_help(&help.replace("{n}", "\n"), self.term_w); } else { sdebugln!("No"); @@ -467,42 +492,60 @@ impl<'a> Help<'a> { fn spec_vals(&self, a: &ArgWithDisplay) -> String { debugln!("Help::spec_vals: a={}", a); let mut spec_vals = vec![]; + if let Some(ref env) = a.env() { + debugln!( + "Help::spec_vals: Found environment variable...[{:?}:{:?}]", + env.0, + env.1 + ); + spec_vals.push(format!( + " [env:{}: {}]", + env.0.to_string_lossy(), + env.1.to_string_lossy() + )); + } if !a.is_set(ArgSettings::HideDefaultValue) { if let Some(pv) = a.default_val() { debugln!("Help::spec_vals: Found default value...[{:?}]", pv); - spec_vals.push(format!(" [default: {}]", - if self.color { - self.cizer.good(pv.to_string_lossy()) - } else { - Format::None(pv.to_string_lossy()) - })); + spec_vals.push(format!( + " [default: {}]", + if self.color { + self.cizer.good(pv.to_string_lossy()) + } else { + Format::None(pv.to_string_lossy()) + } + )); } } if let Some(ref aliases) = a.aliases() { debugln!("Help::spec_vals: Found aliases...{:?}", aliases); - spec_vals.push(format!(" [aliases: {}]", - if self.color { - aliases - .iter() - .map(|v| format!("{}", self.cizer.good(v))) - .collect::>() - .join(", ") - } else { - aliases.join(", ") - })); + spec_vals.push(format!( + " [aliases: {}]", + if self.color { + aliases + .iter() + .map(|v| format!("{}", self.cizer.good(v))) + .collect::>() + .join(", ") + } else { + aliases.join(", ") + } + )); } if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) { if let Some(pv) = a.possible_vals() { debugln!("Help::spec_vals: Found possible vals...{:?}", pv); spec_vals.push(if self.color { - format!(" [values: {}]", - pv.iter() - .map(|v| format!("{}", self.cizer.good(v))) - .collect::>() - .join(", ")) - } else { - format!(" [values: {}]", pv.join(", ")) - }); + format!( + " [values: {}]", + pv.iter() + .map(|v| format!("{}", self.cizer.good(v))) + .collect::>() + .join(", ") + ) + } else { + format!(" [values: {}]", pv.join(", ")) + }); } } spec_vals.join(" ") @@ -581,12 +624,11 @@ impl<'a> Help<'a> { self.longest = 2; let mut ord_m = VecMap::new(); for sc in parser - .subcommands - .iter() - .filter(|s| !s.p.is_set(AppSettings::Hidden)) { - let btm = ord_m - .entry(sc.p.meta.disp_ord) - .or_insert(BTreeMap::new()); + .subcommands + .iter() + .filter(|s| !s.p.is_set(AppSettings::Hidden)) + { + let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new()); self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str())); //self.longest = cmp::max(self.longest, sc.p.meta.name.len()); btm.insert(sc.p.meta.name.clone(), sc.clone()); @@ -679,10 +721,12 @@ impl<'a> Help<'a> { } color!(self, "\nUSAGE:", warning)?; - write!(self.writer, - "\n{}{}\n\n", - TAB, - usage::create_usage_no_title(parser, &[]))?; + write!( + self.writer, + "\n{}{}\n\n", + TAB, + usage::create_usage_no_title(parser, &[]) + )?; let flags = parser.has_flags(); let pos = parser.has_positionals(); @@ -748,21 +792,20 @@ fn copy_until(r: &mut R, w: &mut W, delimiter_byte: u8) -> Co /// - `None`: The reader was consumed. /// - `Some(Ok(0))`: No tag was captured but the reader still contains data. /// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`. -fn copy_and_capture(r: &mut R, - w: &mut W, - tag_buffer: &mut Cursor>) - -> Option> { +fn copy_and_capture( + r: &mut R, + w: &mut W, + tag_buffer: &mut Cursor>, +) -> Option> { use self::CopyUntilResult::*; debugln!("copy_and_capture;"); // Find the opening byte. match copy_until(r, w, b'{') { - // The end of the reader was reached without finding the opening tag. // (either with or without having copied data to the writer) // Return None indicating that we are done. - ReaderEmpty | - DelimiterNotFound(_) => None, + ReaderEmpty | DelimiterNotFound(_) => None, // Something went wrong. ReadError(e) | WriteError(e) => Some(Err(e)), @@ -770,7 +813,6 @@ fn copy_and_capture(r: &mut R, // The opening byte was found. // (either with or without having copied data to the writer) DelimiterFound(_) => { - // Lets reset the buffer first and find out how long it is. tag_buffer.set_position(0); let buffer_size = tag_buffer.get_ref().len(); @@ -778,7 +820,6 @@ fn copy_and_capture(r: &mut R, // Find the closing byte,limiting the reader to the length of the buffer. let mut rb = r.take(buffer_size as u64); match copy_until(&mut rb, tag_buffer, b'}') { - // We were already at the end of the reader. // Return None indicating that we are done. ReaderEmpty => None, @@ -790,17 +831,13 @@ fn copy_and_capture(r: &mut R, // The end of the reader was found without finding the closing tag. // Write the opening byte and captured text to the writer. // Return 0 indicating that nothing was caputred but the reader still contains data. - DelimiterNotFound(not_tag_length) => { - match w.write(b"{") { + DelimiterNotFound(not_tag_length) => match w.write(b"{") { + Err(e) => Some(Err(e)), + _ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) { Err(e) => Some(Err(e)), - _ => { - match w.write(&tag_buffer.get_ref()[0..not_tag_length]) { - Err(e) => Some(Err(e)), - _ => Some(Ok(0)), - } - } - } - } + _ => Some(Ok(0)), + }, + }, ReadError(e) | WriteError(e) => Some(Err(e)), } @@ -850,11 +887,14 @@ impl<'a> Help<'a> { _ => continue, }; - debugln!( - "Help::write_template_help:iter: tag_buf={};", - str::from_utf8(&tag_buf.get_ref()[..tag_length]) - .unwrap_or_default() - ); + debugln!("Help::write_template_help:iter: tag_buf={};", unsafe { + String::from_utf8_unchecked( + tag_buf.get_ref()[0..tag_length] + .iter() + .map(|&i| i) + .collect::>(), + ) + }); match &tag_buf.get_ref()[0..tag_length] { b"?" => { self.writer.write_all(b"Could not decode tag name")?; @@ -863,24 +903,32 @@ impl<'a> Help<'a> { self.write_bin_name(parser)?; } b"version" => { - write!(self.writer, - "{}", - parser.meta.version.unwrap_or("unknown version"))?; + write!( + self.writer, + "{}", + parser.meta.version.unwrap_or("unknown version") + )?; } b"author" => { - write!(self.writer, - "{}", - parser.meta.author.unwrap_or("unknown author"))?; + write!( + self.writer, + "{}", + parser.meta.author.unwrap_or("unknown author") + )?; } b"about" => { - write!(self.writer, - "{}", - parser.meta.about.unwrap_or("unknown about"))?; + write!( + self.writer, + "{}", + parser.meta.about.unwrap_or("unknown about") + )?; } b"long-about" => { - write!(self.writer, - "{}", - parser.meta.long_about.unwrap_or("unknown about"))?; + write!( + self.writer, + "{}", + parser.meta.long_about.unwrap_or("unknown about") + )?; } b"usage" => { write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?; @@ -908,14 +956,18 @@ impl<'a> Help<'a> { self.write_subcommands(parser)?; } b"after-help" => { - write!(self.writer, - "{}", - parser.meta.more_help.unwrap_or("unknown after-help"))?; + write!( + self.writer, + "{}", + parser.meta.more_help.unwrap_or("unknown after-help") + )?; } b"before-help" => { - write!(self.writer, - "{}", - parser.meta.pre_help.unwrap_or("unknown before-help"))?; + write!( + self.writer, + "{}", + parser.meta.pre_help.unwrap_or("unknown before-help") + )?; } // Unknown tag, write it back. r => { diff --git a/src/app/mod.rs b/src/app/mod.rs index e9e26e81..98da8b9c 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1796,6 +1796,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> { fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { None } + fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> { None } fn longest_filter(&self) -> bool { true } fn aliases(&self) -> Option> { if let Some(ref aliases) = self.p.meta.aliases { diff --git a/src/app/parser.rs b/src/app/parser.rs index 8dd3a618..c8fffb67 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -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> { self.flags.iter() } diff --git a/src/app/validator.rs b/src/app/validator.rs index 6227c060..c65ecd8d 100644 --- a/src/app/validator.rs +++ b/src/app/validator.rs @@ -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); diff --git a/src/args/any_arg.rs b/src/args/any_arg.rs index e1721a70..c574f5c3 100644 --- a/src/args/any_arg.rs +++ b/src/args/any_arg.rs @@ -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, &'e OsStr)>>; + fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)>; fn longest_filter(&self) -> bool; fn val_terminator(&self) -> Option<&'e str>; } diff --git a/src/args/arg.rs b/src/args/arg.rs index f16e20a0..85027a0b 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -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; @@ -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), @@ -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 @@ -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!["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. diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs index ec34382c..36977f8e 100644 --- a/src/args/arg_builder/flag.rs +++ b/src/args/arg_builder/flag.rs @@ -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> { @@ -83,10 +89,12 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { fn default_vals_ifs(&self) -> Option, &'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> { 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() { @@ -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)] @@ -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"); } } diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 135feeda..08248973 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -6,14 +6,15 @@ 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>, @@ -21,7 +22,12 @@ pub struct OptBuilder<'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> { @@ -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(()) @@ -132,10 +140,14 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> { fn default_vals_ifs(&self) -> Option, &'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> { 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() { @@ -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)] @@ -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 "); } } diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs index 7ba9d0e6..b107e1ef 100644 --- a/src/args/arg_builder/positional.rs +++ b/src/args/arg_builder/positional.rs @@ -8,7 +8,7 @@ use std::mem; // Internal use Arg; -use args::{ArgSettings, Base, Valued, AnyArg, DispOrder}; +use args::{AnyArg, ArgSettings, Base, DispOrder, Valued}; use INTERNAL_ERROR_MSG; use map::{self, VecMap}; @@ -16,7 +16,8 @@ use map::{self, VecMap}; #[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>, @@ -39,7 +40,8 @@ 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 @@ -47,7 +49,8 @@ impl<'n, 'e> PosBuilder<'n, 'e> { 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 { @@ -74,11 +77,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::>() - .join(" ")) + Cow::Owned( + names + .values() + .map(|n| format!("<{}>", n)) + .collect::>() + .join(" "), + ) } else { Cow::Borrowed(names.values().next().expect(INTERNAL_ERROR_MSG)) } @@ -92,17 +97,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::>() - .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, "...")?; } @@ -143,6 +152,12 @@ 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 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> { None } } diff --git a/src/args/arg_builder/valued.rs b/src/args/arg_builder/valued.rs index 084b6da7..5ea0bb94 100644 --- a/src/args/arg_builder/valued.rs +++ b/src/args/arg_builder/valued.rs @@ -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>, pub val_names: Option>, @@ -20,6 +21,7 @@ pub struct Valued<'a, 'b> pub val_delim: Option, pub default_val: Option<&'b OsStr>, pub default_vals_ifs: Option, &'b OsStr)>>, + pub env: Option<(&'a OsStr, OsString)>, pub terminator: Option<&'b str>, } @@ -36,6 +38,7 @@ impl<'n, 'e> Default for Valued<'n, 'e> { val_delim: None, default_val: None, default_vals_ifs: None, + env: None, terminator: None, } } diff --git a/tests/default_vals.rs b/tests/default_vals.rs index 1040b0b7..cee8adfc 100644 --- a/tests/default_vals.rs +++ b/tests/default_vals.rs @@ -8,8 +8,9 @@ use clap::{App, Arg, ErrorKind}; #[test] fn opts() { let r = App::new("df") - .arg( Arg::from_usage("-o [opt] 'some opt'") - .default_value("default")) + .arg( + Arg::from_usage("-o [opt] 'some opt'").default_value("default"), + ) .get_matches_from_safe(vec![""]); assert!(r.is_ok()); let m = r.unwrap(); @@ -20,8 +21,9 @@ fn opts() { #[test] fn opt_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'") - .default_value("default")) + .arg( + Arg::from_usage("--opt [FILE] 'some arg'").default_value("default"), + ) .get_matches_from_safe(vec!["", "--opt", "value"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -32,8 +34,7 @@ fn opt_user_override() { #[test] fn positionals() { let r = App::new("df") - .arg( Arg::from_usage("[arg] 'some opt'") - .default_value("default")) + .arg(Arg::from_usage("[arg] 'some opt'").default_value("default")) .get_matches_from_safe(vec![""]); assert!(r.is_ok()); let m = r.unwrap(); @@ -44,8 +45,7 @@ fn positionals() { #[test] fn positional_user_override() { let r = App::new("df") - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("default")) + .arg(Arg::from_usage("[arg] 'some arg'").default_value("default")) .get_matches_from_safe(vec!["", "value"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -59,10 +59,11 @@ fn positional_user_override() { fn osstr_opts() { use std::ffi::OsStr; let expected = OsStr::new("default"); - + let r = App::new("df") - .arg( Arg::from_usage("-o [opt] 'some opt'") - .default_value_os(expected)) + .arg( + Arg::from_usage("-o [opt] 'some opt'").default_value_os(expected), + ) .get_matches_from_safe(vec![""]); assert!(r.is_ok()); let m = r.unwrap(); @@ -76,8 +77,9 @@ fn osstr_opt_user_override() { let default = OsStr::new("default"); let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'") - .default_value_os(default)) + .arg( + Arg::from_usage("--opt [FILE] 'some arg'").default_value_os(default), + ) .get_matches_from_safe(vec!["", "--opt", "value"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -91,8 +93,9 @@ fn osstr_positionals() { let expected = OsStr::new("default"); let r = App::new("df") - .arg( Arg::from_usage("[arg] 'some opt'") - .default_value_os(expected)) + .arg( + Arg::from_usage("[arg] 'some opt'").default_value_os(expected), + ) .get_matches_from_safe(vec![""]); assert!(r.is_ok()); let m = r.unwrap(); @@ -106,8 +109,9 @@ fn osstr_positional_user_override() { let default = OsStr::new("default"); let r = App::new("df") - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value_os(default)) + .arg( + Arg::from_usage("[arg] 'some arg'").default_value_os(default), + ) .get_matches_from_safe(vec!["", "value"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -115,14 +119,17 @@ fn osstr_positional_user_override() { assert_eq!(m.value_of("arg").unwrap(), "value"); } -// --- Default if arg is present +// --- Default if arg is present #[test] fn default_if_arg_present_no_default() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value_if("opt", None, "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("[arg] 'some arg'").default_value_if( + "opt", + None, + "default", + )) .get_matches_from_safe(vec!["", "--opt", "some"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -133,9 +140,12 @@ fn default_if_arg_present_no_default() { #[test] fn default_if_arg_present_no_default_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value_if("opt", None, "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("[arg] 'some arg'").default_value_if( + "opt", + None, + "default", + )) .get_matches_from_safe(vec!["", "--opt", "some", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -146,10 +156,12 @@ fn default_if_arg_present_no_default_user_override() { #[test] fn default_if_arg_present_no_arg_with_default() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", None, "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", None, "default"), + ) .get_matches_from_safe(vec![""]); assert!(r.is_ok()); let m = r.unwrap(); @@ -160,10 +172,12 @@ fn default_if_arg_present_no_arg_with_default() { #[test] fn default_if_arg_present_with_default() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", None, "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", None, "default"), + ) .get_matches_from_safe(vec!["", "--opt", "some"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -174,10 +188,12 @@ fn default_if_arg_present_with_default() { #[test] fn default_if_arg_present_with_default_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", None, "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", None, "default"), + ) .get_matches_from_safe(vec!["", "--opt", "some", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -188,10 +204,12 @@ fn default_if_arg_present_with_default_user_override() { #[test] fn default_if_arg_present_no_arg_with_default_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", None, "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", None, "default"), + ) .get_matches_from_safe(vec!["", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -204,9 +222,12 @@ fn default_if_arg_present_no_arg_with_default_user_override() { #[test] fn default_if_arg_present_with_value_no_default() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value_if("opt", Some("value"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("[arg] 'some arg'").default_value_if( + "opt", + Some("value"), + "default", + )) .get_matches_from_safe(vec!["", "--opt", "value"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -217,9 +238,12 @@ fn default_if_arg_present_with_value_no_default() { #[test] fn default_if_arg_present_with_value_no_default_fail() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value_if("opt", Some("value"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("[arg] 'some arg'").default_value_if( + "opt", + Some("value"), + "default", + )) .get_matches_from_safe(vec!["", "--opt", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -230,9 +254,12 @@ fn default_if_arg_present_with_value_no_default_fail() { #[test] fn default_if_arg_present_with_value_no_default_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("[arg] 'some arg'").default_value_if( + "opt", + Some("some"), + "default", + )) .get_matches_from_safe(vec!["", "--opt", "some", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -243,10 +270,12 @@ fn default_if_arg_present_with_value_no_default_user_override() { #[test] fn default_if_arg_present_with_value_no_arg_with_default() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", Some("some"), "default"), + ) .get_matches_from_safe(vec![""]); assert!(r.is_ok()); let m = r.unwrap(); @@ -257,10 +286,12 @@ fn default_if_arg_present_with_value_no_arg_with_default() { #[test] fn default_if_arg_present_with_value_no_arg_with_default_fail() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", Some("some"), "default"), + ) .get_matches_from_safe(vec!["", "--opt", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -271,10 +302,12 @@ fn default_if_arg_present_with_value_no_arg_with_default_fail() { #[test] fn default_if_arg_present_with_value_with_default() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", Some("some"), "default"), + ) .get_matches_from_safe(vec!["", "--opt", "some"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -285,10 +318,12 @@ fn default_if_arg_present_with_value_with_default() { #[test] fn default_if_arg_present_with_value_with_default_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", Some("some"), "default"), + ) .get_matches_from_safe(vec!["", "--opt", "some", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -299,10 +334,12 @@ fn default_if_arg_present_with_value_with_default_user_override() { #[test] fn default_if_arg_present_no_arg_with_value_with_default_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", Some("some"), "default"), + ) .get_matches_from_safe(vec!["", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -313,10 +350,12 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override() { #[test] fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_if("opt", Some("some"), "default")) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_if("opt", Some("some"), "default"), + ) .get_matches_from_safe(vec!["", "--opt", "value", "other"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -329,14 +368,13 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() { #[test] fn default_ifs_arg_present() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("--flag 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_ifs(&[ - ("opt", Some("some"), "default"), - ("flag", None, "flg"), - ])) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("--flag 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_ifs(&[("opt", Some("some"), "default"), ("flag", None, "flg")]), + ) .get_matches_from_safe(vec!["", "--flag"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -347,14 +385,13 @@ fn default_ifs_arg_present() { #[test] fn default_ifs_arg_present_user_override() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("--flag 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_ifs(&[ - ("opt", Some("some"), "default"), - ("flag", None, "flg"), - ])) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("--flag 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_ifs(&[("opt", Some("some"), "default"), ("flag", None, "flg")]), + ) .get_matches_from_safe(vec!["", "--flag", "value"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -365,14 +402,13 @@ fn default_ifs_arg_present_user_override() { #[test] fn default_ifs_arg_present_order() { let r = App::new("df") - .arg( Arg::from_usage("--opt [FILE] 'some arg'")) - .arg( Arg::from_usage("--flag 'some arg'")) - .arg( Arg::from_usage("[arg] 'some arg'") - .default_value("first") - .default_value_ifs(&[ - ("opt", Some("some"), "default"), - ("flag", None, "flg"), - ])) + .arg(Arg::from_usage("--opt [FILE] 'some arg'")) + .arg(Arg::from_usage("--flag 'some arg'")) + .arg( + Arg::from_usage("[arg] 'some arg'") + .default_value("first") + .default_value_ifs(&[("opt", Some("some"), "default"), ("flag", None, "flg")]), + ) .get_matches_from_safe(vec!["", "--opt=some", "--flag"]); assert!(r.is_ok()); let m = r.unwrap(); @@ -386,19 +422,25 @@ fn conditional_reqs_fail() { .version("1.0") .author("F0x06") .about("Arg test") - .arg(Arg::with_name("target") - .takes_value(true) - .default_value("file") - .possible_values(&["file", "stdout"]) - .long("target")) - .arg(Arg::with_name("input") - .takes_value(true) - .required(true) - .long("input")) - .arg(Arg::with_name("output") - .takes_value(true) - .required_if("target", "file") - .long("output")) + .arg( + Arg::with_name("target") + .takes_value(true) + .default_value("file") + .possible_values(&["file", "stdout"]) + .long("target"), + ) + .arg( + Arg::with_name("input") + .takes_value(true) + .required(true) + .long("input"), + ) + .arg( + Arg::with_name("output") + .takes_value(true) + .required_if("target", "file") + .long("output"), + ) .get_matches_from_safe(vec!["test", "--input", "some"]); assert!(m.is_err()); @@ -411,19 +453,25 @@ fn conditional_reqs_pass() { .version("1.0") .author("F0x06") .about("Arg test") - .arg(Arg::with_name("target") - .takes_value(true) - .default_value("file") - .possible_values(&["file", "stdout"]) - .long("target")) - .arg(Arg::with_name("input") - .takes_value(true) - .required(true) - .long("input")) - .arg(Arg::with_name("output") - .takes_value(true) - .required_if("target", "file") - .long("output")) + .arg( + Arg::with_name("target") + .takes_value(true) + .default_value("file") + .possible_values(&["file", "stdout"]) + .long("target"), + ) + .arg( + Arg::with_name("input") + .takes_value(true) + .required(true) + .long("input"), + ) + .arg( + Arg::with_name("output") + .takes_value(true) + .required_if("target", "file") + .long("output"), + ) .get_matches_from_safe(vec!["test", "--input", "some", "--output", "other"]); assert!(m.is_ok()); diff --git a/tests/env.rs b/tests/env.rs new file mode 100644 index 00000000..62833ba0 --- /dev/null +++ b/tests/env.rs @@ -0,0 +1,263 @@ +extern crate clap; + +use std::env; +use std::ffi::OsStr; + +use clap::{App, Arg}; + +#[test] +fn env() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg(Arg::from_usage("[arg] 'some opt'").env("CLP_TEST_ENV")) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn env_os() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'").env_os(OsStr::new("CLP_TEST_ENV")), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn no_env() { + // All the other tests use the presence of the Environment variable... + // we need another variable just in case one of the others is running at the same time... + env::remove_var("CLP_TEST_ENV_NONE"); + + let r = App::new("df") + .arg(Arg::from_usage("[arg] 'some opt'").env("CLP_TEST_ENV_NONE")) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg"), None); +} + +#[test] +fn with_default() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV") + .default_value("default"), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn opt_user_override() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("--arg [FILE] 'some arg'").env("CLP_TEST_ENV"), + ) + .get_matches_from_safe(vec!["", "--arg", "opt"]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 1); + assert_eq!(m.value_of("arg").unwrap(), "opt"); +} + +#[test] +fn positionals() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg(Arg::from_usage("[arg] 'some opt'").env("CLP_TEST_ENV")) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn positionals_user_override() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg(Arg::from_usage("[arg] 'some opt'").env("CLP_TEST_ENV")) + .get_matches_from_safe(vec!["", "opt"]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 1); + assert_eq!(m.value_of("arg").unwrap(), "opt"); +} + +#[test] +fn multiple_one() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV") + .use_delimiter(true) + .multiple(true), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.values_of("arg").unwrap().collect::>(), vec!["env"]); +} + +#[test] +fn multiple_three() { + env::set_var("CLP_TEST_ENV_MULTI1", "env1,env2,env3"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV_MULTI1") + .use_delimiter(true) + .multiple(true), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + vec!["env1", "env2", "env3"] + ); +} + +#[test] +fn multiple_no_delimiter() { + env::set_var("CLP_TEST_ENV_MULTI2", "env1 env2 env3"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV_MULTI2") + .multiple(true), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + vec!["env1 env2 env3"] + ); +} + +#[test] +fn possible_value() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV") + .possible_value("env"), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + + +#[test] +fn not_possible_value() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV") + .possible_value("never"), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_err()); +} + +#[test] +fn validator() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV") + .validator(|s| if s == "env" { + Ok(()) + } else { + Err("not equal".to_string()) + }), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn validator_invalid() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = App::new("df") + .arg( + Arg::from_usage("[arg] 'some opt'") + .env("CLP_TEST_ENV") + .validator(|s| if s != "env" { + Ok(()) + } else { + Err("is equal".to_string()) + }), + ) + .get_matches_from_safe(vec![""]); + + assert!(r.is_err()); +}