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

Use one argument to determine load environment, before setting defaults #4817

Closed
2 tasks done
tgross35 opened this issue Mar 31, 2023 · 2 comments
Closed
2 tasks done
Labels
C-enhancement Category: Raise on the bar on expectations

Comments

@tgross35
Copy link
Contributor

Please complete the following tasks

Clap Version

4.1.13

Describe your use case

I think there is a fairly common use case for environment variable defaults where you want to load a specific .env file based on a command argument. This means that the process would be: parse single argument, error or default if missing -> read .env based on parsed value -> parse rest of arguments -> do env defaults for those arguments -> do hardcoded defaults for those arguments -> error for missing values.

I don't know a good way to do this with Clap. Example use case:

#[derive(Parser, Debug)]
pub struct Config {
    #[clap(long, env = "MYAPP_ENV", default_value = "production")]
    pub mode: Mode
    #[clap(long, env = "MYAPP_PORT", default_value = "1234")]
    pub port: u16,
    #[clap(long, env = "MYAPP_BIND_ADDR", default_value = "0.0.0.0")]
    pub bind_addr: String,
}

#[derive(Clone, Copy, Debug, ValueEnum)]
pub enum Mode {
    Production,
    Development,
    Test
}

Describe the solution you'd like

Some way to parse a single field of the struct - I'm imagining something like a parse_first attribute:

struct Config {
    #[clap(long, env = "MYAPP_ENV", default_value = "production", parse_first = true)]
    pub mode: Mode
    // ...
}

Which could create

// function to parse only the first values
impl Config { pub fn parse_first() -> ConfigParseFirst }
// dummy struct that holds values marked `parse_first`
struct ConfigParseFirst { pub mode: Mode } 
// continue parsing the rest of the values, then go to their defaults
// move `mode` to `Config` and don't reparse to avoid race condition type thingies
impl ConfigParseFirst { pub fn parse_rest(self) -> Config }

And then be used like:

fn main() {
    load_env(".env");
    let tmp_cfg = Config::parse_first();
    match tmp_cfg.mode {
        Production => load_env(".env.production"),
        Development => load_env(".env.development"),
        Test => load_env(".env.test")
    }
    let cfg = tmp_cfg.parse_remaining();
    todo!()
}

Alternatives, if applicable

A callback method could also work, and maybe be simpler. E.g.

struct Config {
    #[clap(long, env = "MYAPP_ENV", default_value = "production", parse_first_callback = set_the_mode)]
    pub mode: Mode
    // ...
}

fn set_the_mode(mode: &Mode) {
    match mode { /* ... */ }
}

Additional Context

No response

@tgross35 tgross35 added the C-enhancement Category: Raise on the bar on expectations label Mar 31, 2023
@epage
Copy link
Member

epage commented Mar 31, 2023

I believe you are looking for a mixture of either

I'm closing in favor of those.

@epage epage closed this as not planned Won't fix, can't repro, duplicate, stale Mar 31, 2023
@tgross35
Copy link
Contributor Author

Good to know those exist, I had no clue what to look for. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: Raise on the bar on expectations
Projects
None yet
Development

No branches or pull requests

2 participants