I would like to add synonyms for the subcommands in my Haskell command line tool. For example summarise
and summarize
should yield the same result. Of course I could just add an entirely separate command summarize
, that appears as an own element in the output of --help
. But maybe there is a more elegant way.
Here is a self-contained example in a stack script opt_ex.hs
:
#!/usr/bin/env stack
-- stack --resolver lts-18.17 script --package optparse-applicative
import Options.Applicative
import Data.Semigroup ((<>))
data Options = CmdGreet GreetArgs | CmdGroot GreetArgs
newtype GreetArgs = GreetArgs String
main :: IO ()
main = do
cmdOpts <- customExecParser (prefs showHelpOnEmpty) (info optParser fullDesc)
runCmd cmdOpts
optParser :: Parser Options
optParser = subparser (
command "greet" (info (CmdGreet <$> sample) (progDesc "Print greeting 1")) <>
command "groot" (info (CmdGroot <$> sample) (progDesc "Print greeting 2"))
)
runCmd :: Options -> IO ()
runCmd o = case o of
CmdGreet opts -> greet opts
CmdGroot opts -> groot opts
greet :: GreetArgs -> IO ()
greet (GreetArgs h) = putStrLn $ "Hello, " h "!"
groot :: GreetArgs -> IO ()
groot (GreetArgs h) = putStrLn $ "Howdy, " h "!"
sample :: Parser GreetArgs
sample = GreetArgs <$> strArgument ( metavar "TARGET" )
You can run this with ./opt_ex.hs greet John
to get Hello, John!
and with ./opt_ex.hs groot John
to get Howdy, John!
. Running ./opt_ex.hs
will give you the following overview:
Usage: opt_ex.hs COMMAND
Available commands:
greet Print greeting 1
groot Print greeting 2
What would be the most elegant way, to add a command gruut
in this example, which behaves exactly like greet
, but produces the least amount of overhead, both in the code and for the user?
Ideally I would like ./opt_ex.hs
to yield something like this:
Usage: opt_ex.hs COMMAND
Available commands:
greet|gruut Print greeting 1
groot Print greeting 2
CodePudding user response:
I don't think you can do this. It works fine for options, because the definition of OptField contains a list of OptName, and adds to that list when you use (<>)
. But the definition of CommandFields, the thing returned by command
, is
data CommandFields a = CommandFields
{ cmdCommands :: [(String, ParserInfo a)]
, cmdGroup :: Maybe String }
Each String name is thus associated with a different ParserInfo. Of course, you can define a variable containing any ParserInfo you like, and reuse it across two commands, so you won't have to repeat the ParserInfo. But as far as optparse-applicative is concerned, those two commands are distinct, so it will list them separately in the help text. For your example, this would look like
optParser = let greeting1 = info (CmdGreet <$> sample) (progDesc "Print greeting 1")
in subparser $
command "greet" greeting1 <>
command "gruut" greeting1 <>
command "groot" (info (CmdGroot <$> sample) (progDesc "Print greeting 2"))
and indeed, when run we see both commands listed:
Usage: optparse COMMAND
Available commands:
greet Print greeting 1
gruut Print greeting 1
groot Print greeting 2