Structopt: `serde`-like support for enum arguments

Created on 3 May 2018  路  12Comments  路  Source: TeXitoi/structopt

Currently, to support an enum field, a user has to

  • Mark it with arg_enum
  • Go into raw to list possible values
  • Depending on use case, go into raw to list default value

Instead being able to do this would be nice

#[derive(StructOpt)]
struct Opt {
   #[structopt(long="format)]
   format: Format,
}

#[derive(StructOpt)]
enum Format {
    Txt,
    Md,
    Html,
}
enhancement

Most helpful comment

Oh, I missed your other comment about try_from_enum (I think it was posted while I was writing mine).

In which case do you need raw for default value?

I've since answered this

Some kind of parse(try_from_enum) can be added to make integration in StructOpt more easy.

an ArgEnum already has a FromStr, so this wouldn't be needed. The issue is more so dropping to raw for listing possible values.

Serialization and deserialisation of enum is above that, it can be used without StructOpt directly with clap, and there is also a lot of other usages outside of cli argument parsing.

I'm mixed. As a user, this is all a part of me describing my CLI, so it feels like it'd be a part of StructOpt. Also as a user, sometimes my enums are at a layer below my CLI and I don't want to have that layer coupled to a CLI crate if I'm even in position to modify the code and if it makes sense in context (newtypes to the rescue?). It would be good to have a general enum crate that structopt is interoperating with and possible re-exporting. In my opinion, the interoperating is key.

All 12 comments

For me, that's not StructOpt job, it should be clap's or an external crate as strum.

Some kind of parse(try_from_enum) can be added to make integration in StructOpt more easy.

In which case do you need raw for default value?

Could you clarify what you do feel structopt's job is?

I feel like using an enum for an argument's choices is a reasonably common enough use case. Probably 1 in 3 CLIs I write (I do a lot of tooling at my job) involve doing this. Currently, to use an arg enum, you have to do this:

    #[structopt(long = "format",
                raw(possible_values = "&Format::variants()", case_insensitive = "true"),
                raw(default_value = "DEFAULT_FORMAT"))]
    pub format: Format,

(the raw on default_value could be simplified in other cases, I just needed it to be platform-specific)

How to use the raw API is non-obvious and I've frequently gotten crashes (panics?) in doing so such that I've shied away from it where possible. Maybe I'm just spoiled by serde but I look at this and know that the macros have sufficient information to streamline this so I can just express what I need through normal Rust means, like enums, Default, etc and the macros pick up on the boiler plate that in some cases can be obtuse.

For me, structopt's job is to change a struct into a clap::App using custom derive, and reparse it back.

Serialization and deserialisation of enum is above that, it can be used without StructOpt directly with clap, and there is also a lot of other usages outside of cli argument parsing.

But I agree that it's a common need for cli argument, and thus having some sugar (as my proposed parse(try_from_enum) to generate the boilerplate) might be a good idea. But we need a commonly accepted interface to do that.

Oh, I missed your other comment about try_from_enum (I think it was posted while I was writing mine).

In which case do you need raw for default value?

I've since answered this

Some kind of parse(try_from_enum) can be added to make integration in StructOpt more easy.

an ArgEnum already has a FromStr, so this wouldn't be needed. The issue is more so dropping to raw for listing possible values.

Serialization and deserialisation of enum is above that, it can be used without StructOpt directly with clap, and there is also a lot of other usages outside of cli argument parsing.

I'm mixed. As a user, this is all a part of me describing my CLI, so it feels like it'd be a part of StructOpt. Also as a user, sometimes my enums are at a layer below my CLI and I don't want to have that layer coupled to a CLI crate if I'm even in position to modify the code and if it makes sense in context (newtypes to the rescue?). It would be good to have a general enum crate that structopt is interoperating with and possible re-exporting. In my opinion, the interoperating is key.

For anyone needing a quick fix until this issue is properly resolved, I've written a small derive crate that ought to work for simple use cases: https://github.com/Palladinium/arg-enum

I don't see how your arg-enum crate is better than strum. With strum, you have EnumVariantNames to give the possible_values to clap, and a lot of other functionnalities.

Yeah, you're right - I hadn't looked into strum thinking it was another cli parser library along the lines of clap and paw, and now I wish I had before sitting down to write my own implementation :laughing:

With strum, and the new raw syntax, you can now:

use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames};

const DEFAULT: &str = "txt";

#[derive(StructOpt, Debug)]
struct Opt {
    #[structopt(
        long,
        possible_values = Format::VARIANTS,
        case_insensitive = true,
        default_value = DEFAULT,
    )]
    format: Format,
}

#[derive(EnumString, EnumVariantNames, Debug)]
#[strum(serialize_all = "kebab_case")]
enum Format {
    Txt,
    Md,
    Html,
}

fn main() {
    println!("{:?}", Opt::from_args());
}

So, OK to close this?

Feel free to reopen if you think something else should be done.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sharksforarms picture sharksforarms  路  6Comments

swfsql picture swfsql  路  3Comments

bestouff picture bestouff  路  6Comments

gnzlbg picture gnzlbg  路  5Comments

coder543 picture coder543  路  9Comments