1

I have complicated command line options, as

data Arguments = Arguments Bool (Maybe SubArguments)
data SubArguments = SubArguments String String

I want to parse these subarguments with a flag:

programName --someflag --subarguments "a" "b"
programName --someflag

I already have

subArgParser = SubArguments <$> argument str <*> argument str
mainParser = MainArgs <$> switch
                  (long "someflag"
                   <> help "Some argument flag")
               <*> ???
                   (long "subarguments"
                   <> help "Sub arguments"

What do I have to write at the ???

Shersh
  • 9,019
  • 3
  • 33
  • 61
Pieter
  • 777
  • 5
  • 17

2 Answers2

2

Your question turned out to be more complicated than you think. Current optparse-applicative API is not supposed to be used with such cases. So you probably may want to change the way you handle CLI arguments or switch to another CLI parsing library. But I will describe most closest way of achieving your goal.

First, you need to read other two SO questions:

1. How to parse Maybe with optparse-applicative

2. Is it possible to have a optparse-applicative option with several parameters?

From first question you know how to parse optional arguments using optional function. From second you learn some problems with parsing multiple arguments. So I will write here several approaches how you can workaround this problem.

1. Naive and ugly

You can represent pair of strings as pair of String type and use just naive show of this pair. Here is code:

mainParser :: Parser Arguments
mainParser = Arguments
    <$> switch (long "someflag" <> help "Some argument flag")
    <*> optional (uncurry SubArguments <$>
                   (option auto $ long "subarguments" <> help "some desc"))

getArguments :: IO Arguments
getArguments = do
    (res, ()) <- simpleOptions "main example" "" "desc" mainParser empty
    return res

main :: IO ()
main = getArguments >>= print

Here is result in ghci:

ghci> :run main --someflag --subarguments "(\"a\",\"b\")"
Arguments True (Just (SubArguments "a" "b"))

2. Less naive

From answer to second question you should learn how pass multiple arguments inside one string. Here is code for parsing:

subArgParser :: ReadM SubArguments
subArgParser = do
    input <- str
    -- no error checking, don't actually do this
    let [a,b] = words input
    pure $ SubArguments a b

mainParser :: Parser Arguments
mainParser = Arguments
    <$> switch (long "someflag" <> help "Some argument flag")
    <*> optional (option subArgParser $ long "subarguments" <> help "some desc")

And here is ghci output:

ghci> :run main --someflag --subarguments "x yyy"
Arguments True (Just (SubArguments "x" "yyy"))

The only bad thing in second solution is that error checking is absent. Thus you can use another general purpose parsing library, for example megaparsec, instead of just let [a,b] = words input.

Community
  • 1
  • 1
Shersh
  • 9,019
  • 3
  • 33
  • 61
  • I asked the author of the library. It's not possible (for now). See the anwer by Paolo Capriotti – Pieter Feb 22 '17 at 23:19
1

It's not possibile, at least not directly. You might find some indirect encoding that works for you, but I'm not sure. Options take arguments, not subparsers. You can have subparsers, but they are introduced by a "command", not an option (i.e. without the leading --).

Paolo Capriotti
  • 4,052
  • 21
  • 25