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

Parse different arguments to the same field from &ArgMatches #3119

Closed
epage opened this issue Dec 9, 2021 · 7 comments
Closed

Parse different arguments to the same field from &ArgMatches #3119

epage opened this issue Dec 9, 2021 · 7 comments
Labels
A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations S-duplicate Status: Closed as Duplicate

Comments

@epage
Copy link
Member

epage commented Dec 9, 2021

Issue by I60R
Sunday Jul 22, 2018 at 06:15 GMT
Originally opened as TeXitoi/structopt#126


Example: there is -v which increases log verbosity, and -q which decreases it, and that should be backed by single log_level field in Options struct.

Alacritty implements that pattern with use of clap and manually filled struct; but with use of structopt there will be no other way as split log_level into two fields: q and v.

I propose something like parse(from_matches = "parser") to avoid that.

#[derive(StructOpt))]
struct Options {
    #[structopt(parse(from_matches = "parse_log_level"))]
    log_level: log::LevelFilter,

    ...
}

fn parse_log_level(matches: &ArgMatches) -> log::LevelFilter {
    match matches.occurrences_of("v") {
        0 => match matches.occurrences_of("q") {
            0 => log::LevelFilter::Warn,
            1 => log::LevelFilter::Error,
            2 | _ => log::LevelFilter::Off
        },
        1 => log::LevelFilter::Info,
        2 => log::LevelFilter::Debug,
        3 | _ => log::LevelFilter::Trace
    }
}

Also, I have a use case where parser like that may greatly simplify my code

@epage
Copy link
Member Author

epage commented Dec 9, 2021

Comment by TeXitoi
Monday Jul 23, 2018 at 14:15 GMT


That's a quite complicated feature to design. Maybe using the parse feature, giving a custom parsing command that takes the 2 parameters and returns the value, with a way to list on a struct field that this field is linked to 2 options.

Fuzzy try:

fn parse_log_level(quiet: u32, verbose: u32) -> log::LevelFilter {
    match (quiet, verbose) {
        (0, 0) => log::LevelFilter::Warn,
        (0, 1) => log::LevelFilter::Error,
        (0, _) => log::LevelFilter::Off,
        (1, _) => log::LevelFilter::Info,
        (2, _) => log::LevelFilter::Debug,
        (_, _) => log::LevelFilter::Trace,
    }
}

#[derive(StructOpt)]
struct Opt {
    #[structopt(
        multiarg(short = "v", long = "verbose", parse(from_occurence)),
        multiarg(short = "q", long = "quiet", parse(from_occurence)),
        parse(from_multiargs = "parse_log_level"),
    )]
    verbose: log::LevelFilter,
}

@epage
Copy link
Member Author

epage commented Dec 9, 2021

Comment by I60R
Monday Jul 23, 2018 at 19:17 GMT


What are your thoughts about delegating q and v fields into separate struct?
That will be very similar to flatten feature that additionally invokes .from() on flattened struct:

#[derive(StructOpt)]
struct Opt {
    #[structopt(collapse(LogLevelOpt)]
    verbose: log::LevelFilter,

    ...
}

#[derive(StructOpt)]
struct LogLevelOpt {
    #[structopt(short = "v", long = "verbose", parse(from_occurences)]
    verbose: u8,

    #[structopt(short = "q", long = "quiet", parse(from_occurences)]
    quiet: u8,
}

impl Into<log::LevelFilter> for LogLevelOpt {
    fn into(self) -> log::LevelFilter {
        match (self.quiet, self.verbose) {
            (0, 0) => log::LevelFilter::Warn,
            (0, 1) => log::LevelFilter::Error,
            (0, _) => log::LevelFilter::Off,
            (1, _) => log::LevelFilter::Info,
            (2, _) => log::LevelFilter::Debug,
            (_, _) => log::LevelFilter::Trace,
        }
    }
}

Also, there might be other version that uses custom function instead of from:

#[derive(StructOpt)]
struct Opt {
    #[structopt(collapse(LogLevelOpt), parse(from_collapsed = "custom_function")]
    verbose: log::LevelFilter,

    ...
}

#[derive(StructOpt)]
struct LogLevelOpt {
    ...
}

fn custom_function(opt: LogLevelOpt) -> log::LevelFilter {
   ...
}

@epage
Copy link
Member Author

epage commented Dec 9, 2021

Comment by TeXitoi
Tuesday Jul 24, 2018 at 15:02 GMT


@I60R That's another path that looks promising. I prefer it to my proposition.

@epage
Copy link
Member Author

epage commented Dec 9, 2021

Comment by eugmes
Monday Jul 26, 2021 at 20:36 GMT


I have another use case that I think may benefit from having multiple arguments apply to the same field, and it is hard or impossible to implement this using multiple separate arguments.

I have a program that's processes some Unicode characters, user should be able to enable or disable processing of some ranges, multiple enable/disable arguments are possible, they should be processes in order they are supplied. For example:

--include-range 100-200 --exclude-range 120-130

The result should be the same as:

--include-range 100-119 --include-range 131-200

I would like to model this as follows:

enum UnicodeRange {...}

enum RangeSpec {
    Included {
        include_range: UnicodeRange,
    },
    Excluded {
        exclude_range: UnicodeRange,
    },
}

struct Opt {
    ...
    #[structopt(???)]
    ranges: Vec[RangeSpec],
    ...
}

I could not find any good way to do this. Note that separate arguments would not work because the order is important.

I've tried to use flatten, but structopt insists on adding subcommands :/.

@epage
Copy link
Member Author

epage commented Dec 9, 2021

Comment by TeXitoi
Monday Jul 26, 2021 at 21:19 GMT


@eugmes this is similar to your use case: TeXitoi/structopt#476

@epage
Copy link
Member Author

epage commented Dec 9, 2021

Comment by eugmes
Monday Jul 26, 2021 at 21:53 GMT


@TeXitoi indeed it is. Thanks a lot!

@epage epage added A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations labels Dec 9, 2021
@epage
Copy link
Member Author

epage commented Dec 13, 2021

This is a duplicate of #3146.

@epage epage closed this as completed Dec 13, 2021
@epage epage added the S-duplicate Status: Closed as Duplicate label Jan 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations S-duplicate Status: Closed as Duplicate
Projects
None yet
Development

No branches or pull requests

1 participant