optparse-applicative icon indicating copy to clipboard operation
optparse-applicative copied to clipboard

Duplicate option displays wrong error message...

Open mgajda opened this issue 5 years ago • 3 comments

I have a parser accepting --request option, and when user accidentally uses it twice, the error message says:

Invalid option `--request'

It should probably report:

Duplicated option is not allowed `--request'

Otherwise user things that the option name is wrong.

mgajda avatar Jun 06 '20 20:06 mgajda

I don't think the current behaviour is wrong per se, the option really is invalid at that stage in the parse – maybe sub-optimal.

I did write something to test for this; but it couldn't distinguish between an option which has been used and an option which is not accessible due to an alternative being used. So the message could be something more like "the option --request can no longer be accessed".

Haven't merged it or put it anywhere yet though.

HuwCampbell avatar Jun 16 '20 00:06 HuwCampbell

I also hit this problem and think there should be a better error message if a valid option is supplied too often.

Here is a popular instance:

$ cabal-plan --ascii info --ascii
Invalid option `--ascii'

$ cabal-plan --help
Usage: cabal-plan [--version] [--show-builtin | --hide-builtin] 
                  [--show-global | --hide-global] [--show-setup | --hide-setup] 
                  [--show-exes | --hide-exes] [--color always|never|auto] 
                  [--ascii | --unicode | --ascii-auto] [COMMAND]

So the option is not "invalid".

Here is a reproducer:

import Options.Applicative

main :: IO ()
main = print =<< do
  execParser $ (`info` header "Try options -o 1 -o 2") $ helper <*> optionO

optionO :: Parser (Maybe String)
optionO =
  optional $ strOption $ mconcat
    [ short 'o'
    , metavar "STRING"
    , help "Output STRING."
    ]
$ ./Main -o 1 -o 2
Invalid option `-o'

andreasabel avatar Sep 17 '21 06:09 andreasabel

Commenting here for others facing a similar issue: I've opted to create two parsers, the first of which is the legitimate one, and the second of which throws an error instantly if invoked.

By chaining them with const, if the flag is ever specified a second time, the second parser is invoked and errors out with a custom message.

myOption :: Parser String
myOption = const <$> actualParser <*> errorIfTwiceParser
  where
    nameMod = long "my-option"
    actualParser = option str nameMod
    errorIfTwiceParser = option (readerError errMsg) (nameMod <> hidden <> value (error "optionOnce: should not happen!"))

It's a bit of a hack, but it works and conveniently avoids mucking around with any optparse internals.

dylant-da avatar Sep 22 '22 10:09 dylant-da