Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add env feature gate #2694

Merged
merged 1 commit into from
Aug 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,29 @@ version-sync = "0.9"
criterion = "0.3.2"

[features]
default = ["std", "suggestions", "color", "unicode_help", "derive", "cargo"]
std = ["indexmap/std"] # support for no_std in a backwards-compatible way
default = [
"std",
"derive",
"cargo",
"color",
"env",
"suggestions",
"unicode_help",
]
debug = ["clap_derive/debug"] # Enables debug messages

# Used in default
std = ["indexmap/std"] # support for no_std in a backwards-compatible way
derive = ["clap_derive", "lazy_static"]
cargo = ["lazy_static"] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
color = ["atty", "termcolor"]
env = [] # Use environment variables during arg parsing
suggestions = ["strsim"]
color = ["atty", "termcolor"]
wrap_help = ["terminal_size", "textwrap/terminal_size"]
unicode_help= ["textwrap/unicode-width"] # Enable if any unicode in help message
derive = ["clap_derive", "lazy_static"]
yaml = ["yaml-rust"]
cargo = ["lazy_static"] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
debug = ["clap_derive/debug"] # Enables debug messages
unicode_help = ["textwrap/unicode-width"] # Enable if any unicode in help message

# Optional
wrap_help = ["terminal_size", "textwrap/terminal_size"]
yaml = ["yaml-rust"]

[profile.test]
opt-level = 1
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,12 +464,16 @@ Then run `cargo build` or `cargo update && cargo build` for your project.

### Optional Dependencies / Features

Disabling optional features can decrease the binary size of `clap` and decrease the compile time. If binary size or compile times are extremely important to you, it is a good idea to disable the feautres that you are not using.

#### Features enabled by default

* **derive**: Enables the custom derive (i.e. `#[derive(Clap)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above.
* **std**: _Not Currently Used._ Placeholder for supporting `no_std` environments in a backwards compatible manner.
* **derive**: Enables the custom derive (i.e. `#[derive(Clap)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above. (builds dependency `clap_derive`)
* **cargo**: Turns on macros that read values from `CARGO_*` environment variables.
* **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`)
* **color**: Turns on colored error messages. You still have to turn on colored help by setting `AppSettings::ColoredHelp`. (builds dependency `termcolor`)
* **env**: Turns on the usage of environment variables during parsing.
* **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`)
* **unicode_help**: Turns on support for unicode characters in help messages. (builds dependency `textwrap`)

To disable these, add this to your `Cargo.toml`:
Expand All @@ -494,9 +498,9 @@ features = ["std", "suggestions", "color"]

#### Opt-in features

* **"regex"**: Enables regex validators. (builds dependency `regex`)
* **"wrap_help"**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
* **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
* **"regex"**: Enables regex validators. (builds dependency `regex`)

### More Information

Expand Down
19 changes: 2 additions & 17 deletions src/build/app/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ bitflags! {
const SUBCOMMAND_PRECEDENCE_OVER_ARG = 1 << 41;
const DISABLE_HELP_FLAG = 1 << 42;
const USE_LONG_FORMAT_FOR_HELP_SC = 1 << 43;
const DISABLE_ENV = 1 << 44;
const INFER_LONG_ARGS = 1 << 45;
const IGNORE_ERRORS = 1 << 46;
const INFER_LONG_ARGS = 1 << 44;
const IGNORE_ERRORS = 1 << 45;
}
}

Expand Down Expand Up @@ -96,8 +95,6 @@ impl_settings! { AppSettings, AppFlags,
=> Flags::COLOR_AUTO,
ColorNever("colornever")
=> Flags::COLOR_NEVER,
DisableEnv("disableenv")
=> Flags::DISABLE_ENV,
DontDelimitTrailingValues("dontdelimittrailingvalues")
=> Flags::DONT_DELIM_TRAIL,
DontCollapseArgsInUsage("dontcollapseargsinusage")
Expand Down Expand Up @@ -583,18 +580,6 @@ pub enum AppSettings {
/// ```
ColorNever,

/// Disables the use of environment variables in the app
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::DisableEnv)
/// .get_matches();
/// ```
DisableEnv,

/// Disables the automatic collapsing of positional args into `[ARGS]` inside the usage string
///
/// # Examples
Expand Down
2 changes: 2 additions & 0 deletions src/build/arg/debug_asserts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ fn assert_app_flags(arg: &Arg) {
checker!(Last requires TakesValue);
checker!(HideDefaultValue requires TakesValue);
checker!(MultipleValues requires TakesValue);
#[cfg(feature = "env")]
checker!(HideEnv requires TakesValue);
#[cfg(feature = "env")]
checker!(HideEnvValues requires TakesValue);
checker!(IgnoreCase requires TakesValue);
}
31 changes: 25 additions & 6 deletions src/build/arg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ pub use self::value_hint::ValueHint;
use std::{
borrow::Cow,
cmp::{Ord, Ordering},
env,
error::Error,
ffi::{OsStr, OsString},
ffi::OsStr,
fmt::{self, Display, Formatter},
str,
sync::{Arc, Mutex},
};
#[cfg(feature = "env")]
use std::{env, ffi::OsString};

// Third Party
#[cfg(feature = "regex")]
Expand Down Expand Up @@ -112,6 +113,7 @@ pub struct Arg<'help> {
pub(crate) default_vals: Vec<&'help OsStr>,
pub(crate) default_vals_ifs: VecMap<(Id, Option<&'help OsStr>, Option<&'help OsStr>)>,
pub(crate) default_missing_vals: Vec<&'help OsStr>,
#[cfg(feature = "env")]
pub(crate) env: Option<(&'help OsStr, Option<OsString>)>,
pub(crate) terminator: Option<&'help str>,
pub(crate) index: Option<usize>,
Expand Down Expand Up @@ -262,6 +264,7 @@ impl<'help> Arg<'help> {
/// let arg = Arg::new("foo").env("ENVIRONMENT");
/// assert_eq!(Some(OsStr::new("ENVIRONMENT")), arg.get_env());
/// ```
#[cfg(feature = "env")]
pub fn get_env(&self) -> Option<&OsStr> {
self.env.as_ref().map(|x| x.0)
}
Expand Down Expand Up @@ -3141,6 +3144,7 @@ impl<'help> Arg<'help> {
/// [`ArgMatches::is_present`]: ArgMatches::is_present()
/// [`Arg::takes_value(true)`]: Arg::takes_value()
/// [`Arg::use_delimiter(true)`]: Arg::use_delimiter()
#[cfg(feature = "env")]
#[inline]
pub fn env(self, name: &'help str) -> Self {
self.env_os(OsStr::new(name))
Expand All @@ -3149,6 +3153,7 @@ impl<'help> Arg<'help> {
/// 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.
#[cfg(feature = "env")]
#[inline]
pub fn env_os(mut self, name: &'help OsStr) -> Self {
self.env = Some((name, env::var_os(name)));
Expand Down Expand Up @@ -3941,6 +3946,7 @@ impl<'help> Arg<'help> {
///
/// If we were to run the above program with `--help` the `[env: MODE]` portion of the help
/// text would be omitted.
#[cfg(feature = "env")]
#[inline]
pub fn hide_env(self, hide: bool) -> Self {
if hide {
Expand Down Expand Up @@ -3980,6 +3986,7 @@ impl<'help> Arg<'help> {
///
/// If we were to run the above program with `$ CONNECT=super_secret connect --help` the
/// `[default: CONNECT=super_secret]` portion of the help text would be omitted.
#[cfg(feature = "env")]
#[inline]
pub fn hide_env_values(self, hide: bool) -> Self {
if hide {
Expand Down Expand Up @@ -4753,6 +4760,7 @@ impl<'help> From<&'help Yaml> for Arg<'help> {
"default_value_if" => yaml_tuple3!(a, v, default_value_if),
"default_value_ifs" => yaml_tuple3!(a, v, default_value_if),
"default_missing_value" => yaml_to_str!(a, v, default_missing_value),
#[cfg(feature = "env")]
"env" => yaml_to_str!(a, v, env),
"value_names" => yaml_vec_or_str!(a, v, value_name),
"groups" => yaml_vec_or_str!(a, v, group),
Expand All @@ -4764,6 +4772,9 @@ impl<'help> From<&'help Yaml> for Arg<'help> {
"last" => yaml_to_bool!(a, v, last),
"value_hint" => yaml_str_parse!(a, v, value_hint),
"hide_default_value" => yaml_to_bool!(a, v, hide_default_value),
#[cfg(feature = "env")]
"hide_env" => yaml_to_bool!(a, v, hide_env),
#[cfg(feature = "env")]
"hide_env_values" => yaml_to_bool!(a, v, hide_env_values),
"hide_possible_values" => yaml_to_bool!(a, v, hide_possible_values),
"overrides_with" => yaml_to_str!(a, v, overrides_with),
Expand Down Expand Up @@ -4955,7 +4966,10 @@ impl<'help> Eq for Arg<'help> {}

impl<'help> fmt::Debug for Arg<'help> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.debug_struct("Arg")
let mut ds = f.debug_struct("Arg");

#[allow(unused_mut)]
let mut ds = ds
.field("id", &self.id)
.field("provider", &self.provider)
.field("name", &self.name)
Expand Down Expand Up @@ -4990,15 +5004,20 @@ impl<'help> fmt::Debug for Arg<'help> {
.field("val_delim", &self.val_delim)
.field("default_vals", &self.default_vals)
.field("default_vals_ifs", &self.default_vals_ifs)
.field("env", &self.env)
.field("terminator", &self.terminator)
.field("index", &self.index)
.field("help_heading", &self.help_heading)
.field("global", &self.global)
.field("exclusive", &self.exclusive)
.field("value_hint", &self.value_hint)
.field("default_missing_vals", &self.default_missing_vals)
.finish()
.field("default_missing_vals", &self.default_missing_vals);

#[cfg(feature = "env")]
{
ds = ds.field("env", &self.env);
}

ds.finish()
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/build/arg/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ bitflags! {
const LAST = 1 << 14;
const HIDE_DEFAULT_VAL = 1 << 15;
const CASE_INSENSITIVE = 1 << 16;
#[cfg(feature = "env")]
const HIDE_ENV_VALS = 1 << 17;
const HIDDEN_SHORT_H = 1 << 18;
const HIDDEN_LONG_H = 1 << 19;
const MULTIPLE_VALS = 1 << 20;
#[cfg(feature = "env")]
const HIDE_ENV = 1 << 21;
}
}
Expand All @@ -51,7 +53,9 @@ impl_settings! { ArgSettings, ArgFlags,
RequireEquals("requireequals") => Flags::REQUIRE_EQUALS,
Last("last") => Flags::LAST,
IgnoreCase("ignorecase") => Flags::CASE_INSENSITIVE,
#[cfg(feature = "env")]
HideEnv("hideenv") => Flags::HIDE_ENV,
#[cfg(feature = "env")]
HideEnvValues("hideenvvalues") => Flags::HIDE_ENV_VALS,
HideDefaultValue("hidedefaultvalue") => Flags::HIDE_DEFAULT_VAL,
HiddenShortHelp("hiddenshorthelp") => Flags::HIDDEN_SHORT_H,
Expand Down Expand Up @@ -107,9 +111,11 @@ pub enum ArgSettings {
/// Possible values become case insensitive
IgnoreCase,
/// Hides environment variable arguments from the help message
#[cfg(feature = "env")]
HideEnv,
/// Hides any values currently assigned to ENV variables in the help message (good for sensitive
/// information)
#[cfg(feature = "env")]
HideEnvValues,
/// The argument should **not** be shown in short help text
HiddenShortHelp,
Expand Down Expand Up @@ -178,10 +184,12 @@ mod test {
"ignorecase".parse::<ArgSettings>().unwrap(),
ArgSettings::IgnoreCase
);
#[cfg(feature = "env")]
assert_eq!(
"hideenv".parse::<ArgSettings>().unwrap(),
ArgSettings::HideEnv
);
#[cfg(feature = "env")]
assert_eq!(
"hideenvvalues".parse::<ArgSettings>().unwrap(),
ArgSettings::HideEnvValues
Expand Down
25 changes: 20 additions & 5 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,24 +526,36 @@ macro_rules! clap_app {

macro_rules! impl_settings {
($settings:ident, $flags:ident,
$( $setting:ident($str:expr) => $flag:path ),+
$(
$(#[$inner:ident $($args:tt)*])*
$setting:ident($str:expr) => $flag:path
),+
) => {
impl $flags {
pub(crate) fn set(&mut self, s: $settings) {
match s {
$($settings::$setting => self.0.insert($flag)),*
$(
$(#[$inner $($args)*])*
$settings::$setting => self.0.insert($flag),
)*
}
}

pub(crate) fn unset(&mut self, s: $settings) {
match s {
$($settings::$setting => self.0.remove($flag)),*
$(
$(#[$inner $($args)*])*
$settings::$setting => self.0.remove($flag),
)*
}
}

pub(crate) fn is_set(&self, s: $settings) -> bool {
match s {
$($settings::$setting => self.0.contains($flag)),*
$(
$(#[$inner $($args)*])*
$settings::$setting => self.0.contains($flag),
)*
}
}
}
Expand All @@ -552,7 +564,10 @@ macro_rules! impl_settings {
type Err = String;
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
match &*s.to_ascii_lowercase() {
$( $str => Ok($settings::$setting), )*
$(
$(#[$inner $($args)*])*
$str => Ok($settings::$setting),
)*
_ => Err(format!("unknown AppSetting: `{}`", s)),
}
}
Expand Down
1 change: 1 addition & 0 deletions src/output/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
fn spec_vals(&self, a: &Arg) -> String {
debug!("Help::spec_vals: a={}", a);
let mut spec_vals = vec![];
#[cfg(feature = "env")]
if let Some(ref env) = a.env {
if !a.is_set(ArgSettings::HideEnv) {
debug!(
Expand Down
1 change: 1 addition & 0 deletions src/parse/matches/matched_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::INTERNAL_ERROR_MSG;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ValueType {
Unknown,
#[cfg(feature = "env")]
EnvVariable,
CommandLine,
DefaultValue,
Expand Down
8 changes: 3 additions & 5 deletions src/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
parse::features::suggestions,
parse::{ArgMatcher, SubCommand},
parse::{Validator, ValueType},
util::{str_to_bool, termcolor::ColorChoice, ArgStr, ChildGraph, Id},
util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id},
INTERNAL_ERROR_MSG, INVALID_UTF8,
};

Expand Down Expand Up @@ -1767,15 +1767,13 @@ impl<'help, 'app> Parser<'help, 'app> {
}
}

#[cfg(feature = "env")]
pub(crate) fn add_env(
&mut self,
matcher: &mut ArgMatcher,
trailing_values: bool,
) -> ClapResult<()> {
if self.app.is_set(AS::DisableEnv) {
debug!("Parser::add_env: Env vars disabled, quitting");
return Ok(());
}
use crate::util::str_to_bool;

self.app.args.args().try_for_each(|a| {
// Use env only if the arg was absent among command line args,
Expand Down
4 changes: 4 additions & 0 deletions src/parse/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
) -> ClapResult<()> {
debug!("Validator::validate");
let mut reqs_validated = false;

#[cfg(feature = "env")]
self.p.add_env(matcher, trailing_values)?;

self.p.add_defaults(matcher, trailing_values);

if let ParseState::Opt(a) = parse_state {
debug!("Validator::validate: needs_val_of={:?}", a);
self.validate_required(matcher)?;
Expand Down
Loading