Clap - unable to map input

Hi, I recently started moving one of my binaries to clap. Unfortunately, I don't seem to understand how Clap's derive feature work. I've seen their example, but I'm not sure how to combine those.
My main issue is that no matter which approach I try, I always end up with an issue stating that I must manually implement std::str::FromStr for my inner Structs/enums.

What I want to achieve is something like:
./binary -foo RepoEnum -bar RepoEnum
People might skip passing either foo or bar (It's also acceptable if the binary allows skipping both).
The Argument passed with foo or bar is an enum similar to:

enum RepoEnum {
Stable,
Head,
Local(PathBuf), // Local(String) would also be ok. 
}

I have tried a lot of approaches. For example I've split the enum into a Local<Stable/Head> enum and a Struct { local: PathBuf } and used grouping to accept exactly one of the two for both foo and bar. However, as long as I have the Local(PathBuf) Variant showing up somewhere it asks me to implement FromStr, which makes me believe that I'm using it incorrectly. Which layout would be better, can someone please give me a hint?

Maybe.

over your enum

#[derive(clap::ArgEnum, ...)]

in your clap struct over your item with the enum.

#[clap(long = "option-name", arg_enum)]

Thank you, but that is too straight-forward, it gives me the usual error. I just haven't included the source-code in my original post, because I tried like 5 different variations of it and my post would become unreadable.

This approach only seems to work with simple enums which do not include inner values like a PathBuf. Do you know how I can work around that?
I do need the ability for people to specify a local path to a repository.

Here is the error for the example you suggested:

#[derive(Parser)]
pub struct Input {
    /// some regular input
    #[clap(long = "bar", arg_enum)]
    bar: Repo,

    /// some special input argument
    #[clap(long = "foo", arg_enum)]
    foo: Option<Repo>,
}

/// Used to decide which Version should be used
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
pub enum Repo {
    /// Use the latest  stable release
    Stable,
    /// Use the current  head from Github
    Head,
    /// Use a local  repository at the given path
    Local(String),
    //Local(PathBuf),
}

This gives errors in the style of:

error[E0277]: the trait bound `utils::Repo: ArgEnum` is not satisfied
   --> src/code/utils.rs:26:11
    |
26  |     foo: Repo,
    |           ^^^^ the trait `ArgEnum` is not implemented for `utils::Repo`
    |
note: required by `clap::ArgEnum::from_str`
   --> /home/zusez4/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-3.1.5/src/derive.rs:425:5
    |
425 |     fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Are you missing the word clap.

"clap::ArgEnum"

Thanks for spotting, that was an unnecessary mistake. Unfortunately, the error message stays the same.

*Edit: Apparently it wasn't even a mistake, I just gave another look to the example from where I took it:

I've used this opportunity to set up a playground with my issue:

I think ArgEnum is intended only for simple enums. To use "complex" enums you'd have to use it as a subcommand enum.

Is the intent that "stable" is converted to RepoEnum::Stable, "head" to RepoEnum::Head and everything else like "something" to RepoEnum::Local("something")? Or do you want the argument to look something like "local=something", or something else?

According to clap's derive tutorial, ArgEnum is meant for situations where you have specific values you want to test for:

If you have arguments of specific values you want to test for, you can derive ArgEnum .

This allows you specify the valid values for that argument. If the user does not use one of those specific values, they will receive a graceful exit with error message informing them of the mistake, and what the possible valid values are

I think you could either manually implement FromStr in std::str - Rust for your enum or derive EnumString in strum - Rust with the default attribute on Other and then use #[clap(parse(try_from_str))] like in https://github.com/clap-rs/clap/blob/v3.1.6/examples/tutorial_derive/04_02_parse.rs

Indeed, my mistake was the expectation that Enum Variants with inner values would be supported by automatically taking the following command-line value as candidate for the inner value.
Then I also had been using ArgGroups wrong in the first attempt, however I now was able to fix both without implementing FromStr manually.Thanks to you and all the friendly people from discord :slight_smile: https://github.com/EnzymeAD/Enzyme/pull/519#issuecomment-1062552345

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.