commander-0.1.0.0: src/Commander.hs
-- |
-- Module: Commander
--
-- re-exports everything defined in @Commander.Params@ and @Commander.Commands@
-- for convenience.
--
module Commander (
-- * Example Usage
-- $usage
module Commander.Params,
module Commander.Commands
) where
import Commander.Params
import Commander.Commands
-- $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=" ++ pass
--
-- Commands 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 'Commander.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"] [] someCommands
--
-- Where 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.
--