I'm looking for a library which uses a monad to abstract over the fuss of command line arguments parsing and help generation. I have the following fairly obvious usage pattern in mind:
main = do
portOrSocket <- Args.run $ do
mbSocket <- Args.read $ Args.Arg "s" "socket" "Description"
mbPort <- Args.read $ Args.Arg "p" "port" "Description"
case mbSocket of
Just socket -> return $ Right socket
Nothing -> case mbPort of
Just port -> return $ Left port
Nothing -> return $ Left defaultPort
...
The above code has all the information needed to handle parsing, validation and usage generation and IMO is fairly easy to comprehend. Unfortunately after looking thru hackage and checking out packages like cmdargs, cmdlib, parseargs, ReadArgs I haven't found anything anywhere close to this. But before diving into implementation I'd like to ensure that I haven't missed anything. So is there a library that exploits a similar approach to the problem?
You can use optparse-applicative
. The most common use pattern looks like this (I'm just copy-and-pasting from a small utility I use):
options :: Parser (String, String)
options = (,)
<$> (strOption $ mconcat [
short 'n',
long "node",
metavar "NODE",
value "127.0.0.1",
showDefaultWith id,
completer (bashCompleter "hostname"),
help "AMQP node to connect to" ] )
<*> (strOption $ mconcat [
short 'q',
long "queue",
metavar "QUEUE",
value "1.0.0",
showDefaultWith id,
help "Queue to initialize" ] )
main = do
(hostName, queue) <-
execParser $ info (helper <*> options) $ mconcat [
fullDesc,
header "The Suns setup utility",
progDesc "Sets up an AMQP node",
footer "Report bugs to [email protected]" ]
...
When I run the compiled program with -h
, I get:
$ suns-admin -h
The Suns setup utility
Usage: suns-admin [-n|--node NODE] [-q|--queue QUEUE]
Sets up an AMQP node
Available options:
-h,--help Show this help text
-n,--node NODE AMQP node to connect to (default: 127.0.0.1)
-q,--queue QUEUE Queue to initialize (default: 1.0.0)
Report bugs to [email protected]
That gives you some idea of some nifty options that you can play with and the nice output that the program generates.
If anybody's interested in solving the problem presented in the question using optparse-applicative
, here's how I achieved that:
import Options.Applicative
getOptions :: Int -> IO (Either Int String)
getOptions defaultPort =
execParser $
info (helper <*> parser defaultPort) $
fullDesc <>
progDesc "Run a content-db server on a socket or a port" <>
header "Run a content-db server"
parser :: Int -> Parser (Either Int String)
parser defaultPort =
portOrSocket <$>
(optional . strOption)
( short 's' <>
long "socket" <>
help "Socket" )
<*>
option
( short 'p' <>
long "port" <>
help "Port" <>
value defaultPort )
where
portOrSocket (Just socket) _ = Right socket
portOrSocket _ port = Left port
main = do
getOptions 43400 >>= \o -> case o of
Left port -> print port
Right socket -> print socket
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With