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 support for env-only args for derive #344

Open
vallentin opened this issue Mar 13, 2024 · 2 comments
Open

Add support for env-only args for derive #344

vallentin opened this issue Mar 13, 2024 · 2 comments

Comments

@vallentin
Copy link
Contributor

(Thanks for bpaf it's the only command-line parser that I tested supporting adjacent commands)

This is already supported by the combinatoric API. However, there is an implicit short or long (as documented) for #[bpaf(env("VAR"))]. That behavior can obviously not be changed, I'm asking for some way to express #[bpaf(env_only("VAR"))].


I essentially have a struct EnvVars, with a bunch of env vars inside the main struct Args.

I would like bpaf derive to:

  • Not generate any implicit short nor long
  • Keep them hidden from the help text

My current workaround is to mix combinatoric and derive.

Here's an example. I know it isn't pretty. But it successfully parses everything using bpaf, and all fields within EnvVars are hidden from the help text:

use bpaf::{Bpaf, Parser};

fn main() {
    let args = args().run();
    println!("{:#?}", args);
}

#[derive(Bpaf, Clone, Debug)]
#[bpaf(options)]
pub struct Args {
    #[bpaf(external(env_vars))]
    vars: EnvVars,
}

#[derive(Bpaf, Clone, Debug)]
struct EnvVars {
    #[bpaf(external(var::<0>))]
    a: Option<String>,
    #[bpaf(external(var::<1>))]
    b: Option<String>,
    #[bpaf(external(var::<2>))]
    c: Option<String>,
}

fn var<const NAME: usize>() -> impl bpaf::Parser<Option<String>> {
    const NAMES: [&str; 3] = ["A", "B", "C"];
    let name = NAMES[NAME];

    bpaf::env(name).argument(name).optional()
}

While I could manually use std::env::var() or use the envy crate, I would love if I could keep everything streamlined using bpaf.

@pacak
Copy link
Owner

pacak commented Mar 13, 2024

At this point I'd implement it something like this, with or without an optional helper inside env_vars

#[derive(Clone, Debug)]
struct EnvVars {
    a: Option<String>,
    b: Option<String>,
    c: Option<String>,
}

fn env_vars() -> impl bpaf::Parser<EnvVars> {
    let a = bpaf::env("A").argument("A").optional();
    let b = bpaf::env("B").argument("B").optional();
    let c = bpaf::env("C").argument("C").optional();
    bpaf::construct!(EnvVars { a, b, c })
}

But that's a workaround as well. I need to think a bit about a better approach. Maybe special case long("") into "don't generate a named argument".

#[derive(Bpaf, Clone, Debug)]
struct EnvVars {
    #[bpaf(env, long(""))]
    a: Option<String>,
    #[bpaf(env, long(""))]
    b: Option<String>,
    #[bpaf(env, long(""))]
    c: Option<String>,
}

@vallentin
Copy link
Contributor Author

I like that approach more, but yeah, like I said, it definitely wasn't pretty.

Personally, I approve of long(""), but I could definitely also see someone being confused by it initially.

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

No branches or pull requests

2 participants