| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Commander
Contents
Description
re-exports everything defined in Commander.Params and Commander.Commands
for convenience.
- module Commander.Params
- module Commander.Commands
Example Usage
The below header gives us the language extensions and imports we need
for basic usage of Commander:
{-# LANGUAGE DataKinds, ScopedTypeVariables #-}
module Main where
import qualified Data.Map as Map
import Commander.Params (Flag(..), Value(..))
import Commander.Commands (Command(..), commands, command, help, run, evalCommand)The next step is to define the various commands that we care to match against, which looks something like this:
someCommands :: Command (IO ())
someCommands = commands $ do
command "repeat" $ do
help "Repeat a string n times"
run $
\(Value str :: Value "value to repeat" String)
(Flag n :: Flag '["n"] "times to repeat" Int) ->
sequence_ $ replicate n (putStrLn str)
command "calculate" $ do
help "perform calculations"
command "add" $ do
help "add two numbers"
run $
\(Value n1 :: Value "number 1" Int)
(Value n2 :: Value "number 2" Int)
(Flag verbose :: Flag '["v", "verbose"] "verbose mode" Bool) ->
if verbose
then putStrLn $ (show n1) ++ " + " ++ (show n2) ++ " = " ++ (show $ n1 + n2)
else putStrLn (show $ n1 + n2)
command "multiply" $ do
help "multiply two numbers"
run $
\(Value n1 :: Value "number 1" Int)
(Value n2 :: Value "number 2" Int) ->
putStrLn $ (show n1) ++ " x " ++ (show n2) ++ " = " ++ (show $ n1 * n2)
command "login" $ do
help "pretend authentication"
run $
\(Value username :: Value "Username" String)
(Flag mPassword :: Flag '["p", "password"] "Password" (Maybe String)) -> do
pass <- case mPassword of
Just password -> return password
Nothing -> getLine
putStrLn $ "logging in with username=" ++ username ++ " password=" ++ passCommands can be arbitrary nested, making it super easy to define subcommands as far down as you like.
Using a couple of pre-baked parameter types from Params, namely Value and Flag,
we define in the function signature itself additional values and flags that we expect,
each fully typed and with help text (the Flag type in addition state which flags it will
try to match against).
If you prefer alternate behaviour to the provided types, it is easy to create your own custom alternatives; it's simply a case of making your custom types instances of a few very basic typeclasses.
The return types of the provided functions must match, so that we know what we're getting back
when we try executing a command; this is the only parameter needed by the Command type.
Making use of the above, one could do the following:
runCommand :: [String] -> [(String,String)] -> Command (IO ()) -> IO ()
runCommand vals flags cmds = case evalCommand vals (Map.fromList flags) cmds of
Left err -> putStrLn ("Error: " ++ show err)
Right res -> res
main :: IO ()
main = do
putStrLn "\nRepeat Command:"
runCommand ["repeat", "hello there"] [("n","2")] someCommands
putStrLn "\nAdd numbers:"
runCommand ["calculate", "add", "12", "13"] [] someCommands
putStrLn "\nAdd numbers (verbosely):"
runCommand ["calculate", "add", "12", "13"] [("verbose", "")] someCommands
putStrLn "\nMultiply numbers:"
runCommand ["calculate", "multiply", "12", "13"] [] someCommands
putStrLn "\nPretend Auth (password provided):"
runCommand ["login", "james"] [("p", "lemons")] someCommands
putStrLn "\nPretend Auth (please type a random string):"
runCommand ["login", "james"] [] someCommandsWhere we first define a function that makes use of evalCommand to match the provided
flags and values against a specific command and either run it or return an error, and
either prints the error or runs the resulting IO action.
This library has no opinion on how a text based command is parsed into a path list and
Map of flags, and so the user is free to select an approach that works best for them.
module Commander.Params
module Commander.Commands