clap-rs / clap

A full featured, fast Command Line Argument Parser for Rust
docs.rs/clap
Apache License 2.0
14.05k stars 1.03k forks source link

Add method to parse environment variables from custom source #4607

Open robertkiel opened 1 year ago

robertkiel commented 1 year ago

Please complete the following tasks

Clap Version

4.0.32

Describe your use case

Rust code might run as a standalone application as well as a WebAssembly module in the browser or in any other WebAssembly runtime.

This comes with the advantage that WebAssembly modules and standalone applications share the same code and benefit from the stability and safety of Rust.

Within WebAssembly, in most cases the execution environment cannot access process variables such as argv and environment variables - which are very often used for configuration - , hence they somehow need to get passed into the WebAssembly module. Fortunately, this shim functionaliy is only necessary when compiling to WebAssembly.

Describe the solution you'd like

Since std::env::vars() or std::env::vars_os() are not available, add a method that allows passing environment variables from a custom source into clap.

Example

use std::env;
use std::collections::HashMap;
use clap::{Command, Arg};

let mut cmd = Command::new("myprog")
    .arg(Arg::new("data_dir")
        .long("data_dir")
        .env("MYPROG_DATA_DIR"));

// not necessary, just for demonstration
env::set_var("MYPROG_DATA_DIR", "/home/user/Work/clap");

// might be populated with different data
let env_vars = HashMap::from_iter(env::vars_os());
cmd.update_env_from(env_vars);

let m = cmd.get_matches_from(vec!["foo"]);
assert_eq!("/home/user/Work/clap", m.get_one::<String>("data_dir").unwrap());

Alternatives, if applicable

No response

Additional Context

See #4605 for sample implementation.

This functionality could be hidden by a wasm feature flag.

epage commented 1 year ago

Within WebAssembly, in most cases the execution environment cannot access process variables such as argv and environment variables - which are very often used for configuration - , hence they somehow need to get passed into the WebAssembly module. Fortunately, this shim functionaliy is only necessary when compiling to WebAssembly.

Ok, so this is not wasm in the browser (where there is no environment) but embedded within a desktop application where the environment needs to be proxied.

epage commented 1 year ago

See https://github.com/clap-rs/clap/pull/4605 for sample implementation.

One aspect of that that is weird to me, as an API, is that its updating the envs already stored. I think I'd rather we set the env to use and use that instead. As this is a special use case on top of special use case, I would want to limit the impact of this on others (note that testing would be another use case). You mentioned limiting it to wasm but this has use outside of wasm and I tend to avoid conditional compilation (we already have too much in clap). I have been exploring ideas to make new features cheaper and this would be a good first use of the idea. Buried in that page is a plugin system where a Command and an Arg support any data being put into them using something like an AnyMap. I've been experimenting with it in one of my tools to get some experience before baking it into clap's API (1, 2).

The above does require setting the env source before adding any other args. We could re-evaluate when we read the environment to make this more flexible on when either is called.

robertkiel commented 1 year ago

The above does require setting the env source before adding any other args. We could re-evaluate when we read the environment to make this more flexible on when either is called.

It works mostly like before:

... with the only difference that first all Arg instances get created and then we insert the envs and then we add the --args

robertkiel commented 1 year ago

Within WebAssembly, in most cases the execution environment cannot access process variables such as argv and environment variables - which are very often used for configuration - , hence they somehow need to get passed into the WebAssembly module. Fortunately, this shim functionaliy is only necessary when compiling to WebAssembly.

Ok, so this is not wasm in the browser (where there is no environment) but embedded within a desktop application where the environment needs to be proxied.

The idea was to have one Rust source code that is normally fed with argv + environment variables. But when running in the browser, the browser might read some .env file or have it stored in its local storage and thus mimics environment variables used for configuration. In contrast, a standalone WebAssembly execution environment would just forward all argv values and all environment values to the WebAssembly module.

epage commented 1 year ago

FYI I've created #4793 as an issue dedicated to that plugin system idea I mentioned. Our plan is to move Arg::env support out of the core of clap.

Kinrany commented 10 months ago

As mentioned in #5104, another use case is implementing Default for a type that derives Parser and provides default values.

I have to mention that I was surprised that parse_from does not already ignore the environment. In my model env adds a second source of data to be parsed in addition to CLI arguments, and I expected parse_from to accept an iterator that combines all the data.