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 #126

Closed
I60R opened this issue Jul 22, 2018 · 7 comments
Closed

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

I60R opened this issue Jul 22, 2018 · 7 comments
Labels
enhancement We would love to have this feature! Feel free to supply a PR need-design The concrete desing is uncertain, please get involved in the discussion before sending a PR

Comments

@I60R
Copy link

I60R commented Jul 22, 2018

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

@TeXitoi TeXitoi added the enhancement We would love to have this feature! Feel free to supply a PR label Jul 23, 2018
@TeXitoi
Copy link
Owner

TeXitoi commented Jul 23, 2018

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,
}

@I60R
Copy link
Author

I60R commented Jul 23, 2018

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 {
   ...
}

@TeXitoi
Copy link
Owner

TeXitoi commented Jul 24, 2018

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

@CreepySkeleton CreepySkeleton added the need-design The concrete desing is uncertain, please get involved in the discussion before sending a PR label Dec 29, 2019
@eugmes
Copy link

eugmes commented Jul 26, 2021

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 :/.

@TeXitoi
Copy link
Owner

TeXitoi commented Jul 26, 2021

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

@eugmes
Copy link

eugmes commented Jul 26, 2021

@TeXitoi indeed it is. Thanks a lot!

@TeXitoi
Copy link
Owner

TeXitoi commented Jan 18, 2022

This is an enhancement, and structopt is now feature frozen.

@TeXitoi TeXitoi closed this as completed Jan 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement We would love to have this feature! Feel free to supply a PR need-design The concrete desing is uncertain, please get involved in the discussion before sending a PR
Projects
None yet
Development

No branches or pull requests

4 participants