docopt.rs icon indicating copy to clipboard operation
docopt.rs copied to clipboard

When repeated short and long =value flags are aliases, only one is deserialised

Open OJFord opened this issue 7 years ago • 2 comments

Sorry for the title... essentially when [-a... | --long-a=<val>] the value is only deserialised for the one specified in the struct. Contrast [-b | --bee] as Boolean flags, when the value of whichever is given is available as e.g. flag_bee; flag_b need not be specified.


Here's a reproduction:

[dependencies]
docopt = { version = "1.0.1"}
log = {version = "0.4.5"}
serde = { version = "1.0.76" }
serde_derive = { version = "1.0.76" }
stderrlog = { version = "0.4.1"}
extern crate docopt;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
extern crate stderrlog;

use docopt::Docopt;

const USAGE: &'static str = "
Reproduction.

Usage:
  repro [-q | --quiet | -v... | --verbosity=<v>]

Options:
  -q --quiet                Disable stderr logging.
  -v... --verbosity=<v>     Verbosity of stderr logging.
";

#[derive(Debug, Deserialize)]
struct Args {
    flag_quiet: bool,
    flag_verbosity: usize,
}

fn main() {
   let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    println!("{:?}", args);

    stderrlog::new()
            .module(module_path!())
            .quiet(args.flag_quiet)
            .timestamp(stderrlog::Timestamp::Second)
            .verbosity(args.flag_verbosity)
            .init()
            .unwrap();
    trace!("trace message");
    debug!("debug message");
    info!("info message");
    warn!("warn message");
    error!("error message");
}

Then:

$ cargo run -- --verbosity=2
Args { flag_quiet: false, flag_verbosity: 2 }
2018-09-03T20:50:56+01:00 - INFO - info message
2018-09-03T20:50:56+01:00 - WARN - warn message
2018-09-03T20:50:56+01:00 - ERROR - error message

so far so good.

$ cargo run -- -vvv
Args { flag_quiet: false, flag_verbosity: 0 }
2018-09-03T20:51:43+01:00 - ERROR - error message

Not so good. I thought maybe the issue was using the short while deserialising the long name, but:

$ cargo run -- -q
Args { flag_quiet: true, flag_verbosity: 0 }

so it does appear to be the interaction between ... and =<v>.


Less-than-ideal workaround with particular respect to integer flags, just in case someone stumbles here with a similar use case:

  • separate lines in Options
  • add flag_v: usize, toArgs
  • .verbosity(args.flag_verbosity + args.flag_v) (since docopt takes care of at most one being non-zero)

OJFord avatar Sep 03 '18 20:09 OJFord

I don't think this is a bug. A flag can't both simultaneously accept a value and be a countable switch AFAIK.

BurntSushi avatar Sep 03 '18 22:09 BurntSushi

Thanks for the fast response. I could readily accept that, when you put it like that, but it does seem to work in the Python implementation.

See here - not allowed both, but works when you delete one or other.

Of course, the python implementation (and by extension the online thing) differ in not having serde build a struct of args and flags.

So I suppose it's really a question for you & team whether it should be allowed or not.

If I may advocate for my desired behaviour: I'd say it's consistent with:

-q --quiet  Be quiet.

implying that either flag can be used and be accessible on Args#<either>.

Supporting it would of course mean that it'd only work for integer value flags; not a mix of countable switch and string flag, for example.


Feel free to close if not something you want to support, understood.

OJFord avatar Sep 04 '18 18:09 OJFord