I am writing a commandline program that takes multiple subcommands, which take flags/arguments.
The program should also take some 'global-flags' that are applicable to all subcommands. For examples:
myProgram --configfile=~/.customrc UPLOADFILE --binary myfile.x
myProgram --configfile=~/.customrc SEARCH --regex "[a-z]+"
in this example, the subcommands are UPLOADFILE
and SEARCH
, and configfile
is relevant to both subcommands, and binary
and regex
applicable to the specific subcommands.
I feel this must be possible with this library, but I am struggling to work out what to put where! I am relatively new to Haskell, trying to get my head around applicatives, and it making my brain hurt :)
There is an example of subcommands here in the documentation for the
module, but I can't seem to work out how to get the global-flags to work.
If someone could point me towards a small working example, or insight into how I shoud structure code to do this, I would be very grateful, I am finding these higher order functions slightly magical!
Many thanks for your time. Best wishes,
Mike
The documentation you linked has this to say:
Commands are useful to implement command line programs with multiple
functions, each with its own set of options, and possibly some global
options that apply to all of them.
Although it does not say precisely how those options should be applied. If you look at the types, however, you can gain some insight. The example indicates to use subparser
, whose type is Mod CommandFields a -> Parser a
. This probably doesn't say much (especially the left hand side) but crucially this function just produces a Parser
- so you can combine it with other parsers as you normally would:
data Subcommand
= Upload { binary :: String }
| Search { regex :: String } deriving Show
data Options = Options
{ configFile :: FilePath
, subcommand :: Subcommand
} deriving Show
commandO = Options <$> configFileO <*> subcommandO
configFileO = strOption
( long "configfile"
<> help "Filepath of configuration file"
)
subcommandO :: Parser Subcommand
subcommandO = subparser ...
Defining subcommands themselves is fairly straightforward - I just copied the example from the docs and renamed some things in accordance with your specific example:
subcommandO =
subparser
( command "UPLOADFILE" (info uploadO
( progDesc "Upload a file" ))
<> command "SEARCH" (info searchO
( progDesc "Search in a file" ))
)
uploadO = Upload <$>
( strOption
( long "binary"
<> help "Binary file to upload"
)
)
searchO = Upload <$>
( strOption
( long "regex"
<> help "Regular expression to search for"
)
)
main = execParser opt >>= print where
opt = info (helper <*> commandO)
( fullDesc
<> progDesc "Example for multiple subcommands"
<> header "myProgram" )
Running this program gives the following:
>:main --configfile=~/.customrc UPLOADFILE --binary myfile.x
Options {configFile = "~/.customrc", subcommand = Upload {binary = "myfile.x"}}
>:main --configfile=~/.customrc SEARCH --regex "[a-z]+"
Options {configFile = "~/.customrc", subcommand = Upload {binary = "[a-z]+"}}
>:main --help
myProgram
Usage: <interactive> --configfile ARG COMMAND
Example for multiple subcommands
Available options:
-h,--help Show this help text
--configfile ARG Filepath of configuration file
Available commands:
UPLOADFILE Upload a file
SEARCH Search in a file
*** Exception: ExitSuccess