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 method to parse environment variables from custom source #4607

Open
2 tasks done
robertkiel opened this issue Jan 5, 2023 · 6 comments
Open
2 tasks done

Add method to parse environment variables from custom source #4607

robertkiel opened this issue Jan 5, 2023 · 6 comments
Labels
A-builder Area: Builder API C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing

Comments

@robertkiel
Copy link

robertkiel commented Jan 5, 2023

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.

@robertkiel robertkiel added the C-enhancement Category: Raise on the bar on expectations label Jan 5, 2023
@epage epage added A-builder Area: Builder API S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing labels Jan 5, 2023
@epage
Copy link
Member

epage commented Jan 5, 2023

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
Copy link
Member

epage commented Jan 5, 2023

See #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
Copy link
Author

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:

  • while constructing instances of Arg are created, the environment variable names, and if present, their values get stored into arg.env
  • in a second step, we feed in the --args

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

@robertkiel
Copy link
Author

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
Copy link
Member

epage commented Mar 27, 2023

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
Copy link

Kinrany commented Nov 7, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-builder Area: Builder API C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing
Projects
None yet
Development

No branches or pull requests

3 participants