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 is_env_present to ArgMatches #1833

Closed
wants to merge 1 commit into from

Conversation

davidMcneil
Copy link
Contributor

Closes #1345

Signed-off-by: David McNeil mcneil.david2@gmail.com

@davidMcneil davidMcneil mentioned this pull request Apr 16, 2020
@davidMcneil davidMcneil changed the title Add is_env_var set to ArgMatches Add is_env_var_present to ArgMatches Apr 16, 2020
@davidMcneil davidMcneil force-pushed the master branch 2 times, most recently from 5998f07 to 6da733c Compare April 16, 2020 12:17
@CreepySkeleton
Copy link
Contributor

So far looks good, but let's rename it to is_env to be consistent with Arg::env.

Also, I would go with this description

/// Returns true if, and only if, the value was supplied through an environment variable. 
///
/// If the value was either supplied through a command line option or was set with a default_value
/// or was not supplied at all, returns false.

Comment on lines 139 to 143
pub fn set_env_set(&mut self, arg: &Id) {
let ma = self.entry(arg).or_insert(MatchedArg::new());
ma.env_set = true;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this helper? It's a two line function and you're using it in only one place.

FYI: if a function is not supposed to be exported to user (it's not a part of public API), you should use pub(crate) but not pub.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this helper?

I liked the encapsulation it provided. Otherwise, I had to bring MatchedArg into parser.rs. Happy to remove it if you prefer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name here is not readable. Once you rename the function and the field, you should probably rename this.

Signed-off-by: David McNeil <mcneil.david2@gmail.com>
@davidMcneil davidMcneil changed the title Add is_env_var_present to ArgMatches Add is_env_present to ArgMatches Apr 16, 2020
@davidMcneil
Copy link
Contributor Author

Also, I would go with this description

/// Returns true if, and only if, the value was supplied through an environment variable. 
///
/// If the value was either supplied through a command line option or was set with a default_value
/// or was not supplied at all, returns false.

This actually is different than how is_env_present behaves. is_env_present only checks if any value for the argument was from the environment variable. If the user set the environment value and the value on the command line is_env_present would still be true (I changed the examples to make this clear).

@CreepySkeleton
Copy link
Contributor

is_env_present only checks if any value for the argument was from the environment variable.

Then this function is pointless since it would be a synonym for

std::env::var("ENV_VAR") == Err(ValError::NotPresent)

No point to bring it in clap 🤷

@CreepySkeleton
Copy link
Contributor

What is your use case? I suspect the semantic I described is the one you really need.

@davidMcneil
Copy link
Contributor Author

Then this function is pointless since it would be a synonym for

While this is mostly true, there could potentially be a race condition from when clap reads the env var to when this check is called. I admit that this would be very rare/odd.

The real problem is that if all you have is an ArgMatches how do you determine the name of the environment variable for a specific argument? You have to keep a separate lookup.

What is your use case?

I am trying to distinguish between when an argument's value was set with an environment variable vs when it was set with a default value. One way to do this would be to check if the environment variable was set.

I suspect the semantic I described is the one you really need.

Yes, this would work too and I agree would be more useful. But because arguments can have multiple values does this basically just ensure that the first argument in values was from the environment variable? How does that work with multiple?

@CreepySkeleton
Copy link
Contributor

CreepySkeleton commented Apr 16, 2020

While this is mostly true, there could potentially be a race condition from when clap reads the env var to when this check is called. I admit that this would be very rare/odd.

clap never sets/unsets environment variables. If it was available just before clap started, it is available just after it has finished. You can argue here, "What if I set an env var from inside a validator?". Well if you need to do that, something is very wrong with your design already.

The real problem is that if all you have is an ArgMatches how do you determine the name of the environment variable for a specific argument?

You are kinda the developer of your CLI, aren't you? 😄 How can you not know it? You define these arguments, you define the env name, therefore you know it.

I am trying to distinguish between when an argument's value was set with an environment variable vs when it was set with a default value. One way to do this would be to check if the environment variable was set.

The proper way to do this is adding is_default_value which returns true if, and only if, the arg was not supplied at all and was set via "default value". I would accept such a PR.

I don't really think that introducing a full synonym of a well known function from std will get us anywhere, no offense.

How does that work with multiple?

If you ask me, env variables are meant to be fallbacks, not additional_source; the correct behavior here is: command line replaces env var as env was never used. But the simple test proves me wrong:

use clap::{App, Arg};

fn main() {
    std::env::set_var("ENV_VAR", "val_env");
    
    let m = App::new("app")
        .arg(
            Arg::with_name("test")
                .env("ENV_VAR") 
                .min_values(0)
        )
        .get_matches_from(&["test", "val1", "val2"]);
    
    let values =  m.values_of("test").unwrap().collect::<Vec<_>>();
    assert_eq!(values, &["val1", "val2", "val_env"]);
}

I'm going to raise an issue about that.

@davidMcneil
Copy link
Contributor Author

How can you not know it? You define these arguments, you define the env name, therefore you know it.

If you are iterating over a list of argument names and getting matches from ArgMatches it would be nice to not have to keep a separate mapping of argument name to environment name. But yes you are right ultimately you know it 😄

The proper way to do this is adding is_default_value which returns true if, and only if, the arg was not supplied at all and was set via "default value".

This sounds like a better approach, thanks. I will close this PR and work on that solution.

If you ask me, env variables are meant to be fallbacks, not additional_source

Yes! Completely agree. This was very confusing to me and I would love to see it behave the way you describe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Differentiate between default_value and env in ArgMatches
3 participants