{-# LANGUAGE ScopedTypeVariables, CPP #-}
{-|
    This module constructs command lines. You may either use the helper functions
    ('flagNone', 'flagOpt', 'mode' etc.) or construct the type directly. These
    types are intended to give all the necessary power to the person constructing
    a command line parser.

    For people constructing simpler command line parsers, the module
    "System.Console.CmdArgs.Implicit" may be more appropriate.

    As an example of a parser:

    @
    arguments :: 'Mode' [(String,String)]
    arguments = 'mode' \"explicit\" [] \"Explicit sample program\" ('flagArg' (upd \"file\") \"FILE\")
        ['flagOpt' \"world\" [\"hello\",\"h\"] (upd \"world\") \"WHO\" \"World argument\"
        ,'flagReq' [\"greeting\",\"g\"] (upd \"greeting\") \"MSG\" \"Greeting to give\"
        ,'flagHelpSimple' ((\"help\",\"\"):)]
        where upd msg x v = Right $ (msg,x):v
    @

    And this can be invoked by:

    @
    main = do
        xs <- 'processArgs' arguments
        if (\"help\",\"\") \`elem\` xs then
            print $ 'helpText' [] 'HelpFormatDefault' arguments
         else
            print xs
    @

    /Groups/: The 'Group' structure allows flags/modes to be grouped for the purpose of
    displaying help. When processing command lines, the group structure is ignored.

    /Modes/: The Explicit module allows multiple mode programs by placing additional modes
    in 'modeGroupModes'. Every mode is allowed sub-modes, and thus multiple levels of mode
    may be created. Given a mode @x@ with sub-modes @xs@, if the first argument corresponds
    to the name of a sub-mode, then that sub-mode will be applied. If not, then the arguments
    will be processed by mode @x@. Consequently, if you wish to force the user to explicitly
    enter a mode, simply give sub-modes, and leave 'modeArgs' as @Nothing@. Alternatively, if
    you want one sub-mode to be selected by default, place all it's flags both in the sub-mode
    and the outer mode.

    /Parsing rules/: Command lines are parsed as per most GNU programs. Short arguments single
    letter flags start with @-@, longer flags start with @--@, and everything else is considered
    an argument. Anything after @--@ alone is considered to be an argument. For example:

  > -f --flag argument1 -- --argument2

    This command line passes one single letter flag (@f@), one longer flag (@flag@) and two arguments
    (@argument1@ and @--argument2@).
-}
module System.Console.CmdArgs.Explicit(
    -- * Running command lines
    process, processArgs, processValue, processValueIO,
    -- * Constructing command lines
    module System.Console.CmdArgs.Explicit.Type,
    flagHelpSimple, flagHelpFormat, flagVersion, flagNumericVersion, flagsVerbosity,
    -- * Displaying help
    module System.Console.CmdArgs.Explicit.Help,
    -- * Utilities for working with command lines
    module System.Console.CmdArgs.Explicit.ExpandArgsAt,
    module System.Console.CmdArgs.Explicit.SplitJoin,
    Complete(..), complete
    ) where

import System.Console.CmdArgs.Explicit.Type
import System.Console.CmdArgs.Explicit.Process
import System.Console.CmdArgs.Explicit.Help
import System.Console.CmdArgs.Explicit.ExpandArgsAt
import System.Console.CmdArgs.Explicit.SplitJoin
import System.Console.CmdArgs.Explicit.Complete
import System.Console.CmdArgs.Default
import System.Console.CmdArgs.Helper
import System.Console.CmdArgs.Text
import System.Console.CmdArgs.Verbosity

import Control.Monad
import Data.Char
import Data.Maybe
import System.Environment
import System.Exit
import System.IO


-- | Process the flags obtained by @'getArgs'@ and @'expandArgsAt'@ with a mode. Displays
--   an error and exits with failure if the command line fails to parse, or returns
--   the associated value. Implemented in terms of 'process'. This function makes
--   use of the following environment variables:
--
-- * @$CMDARGS_COMPLETE@ - causes the program to produce completions using 'complete', then exit.
--   Completions are based on the result of 'getArgs', the index of the current argument is taken
--   from @$CMDARGS_COMPLETE@ (set it to @-@ to complete the last argument), and the index within
--   that argument is taken from @$CMDARGS_COMPLETE_POS@ (if set).
--
-- * @$CMDARGS_HELPER@\/@$CMDARGS_HELPER_/PROG/@ - uses the helper mechanism for entering command
--   line programs as described in "System.Console.CmdArgs.Helper".
processArgs :: Mode a -> IO a
processArgs :: Mode a -> IO a
processArgs Mode a
m = do
    [(String, String)]
env <- IO [(String, String)]
getEnvironment
    case String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"CMDARGS_COMPLETE" [(String, String)]
env of
        Just String
x -> do
            [String]
args <- IO [String]
getArgs
            let argInd :: Int
argInd = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe ([String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
args Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$ String -> Maybe Int
forall a. Read a => String -> Maybe a
readMay String
x
                argPos :: Int
argPos = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe (if Int
argInd Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
argInd Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< [String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
args then String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([String]
args [String] -> Int -> String
forall a. [a] -> Int -> a
!! Int
argInd) else Int
0) (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$
                         String -> Maybe Int
forall a. Read a => String -> Maybe a
readMay (String -> Maybe Int) -> Maybe String -> Maybe Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"CMDARGS_COMPLETE_POS" [(String, String)]
env
            [Complete] -> IO ()
forall a. Show a => a -> IO ()
print ([Complete] -> IO ()) -> [Complete] -> IO ()
forall a b. (a -> b) -> a -> b
$ Mode a -> [String] -> (Int, Int) -> [Complete]
forall a. Mode a -> [String] -> (Int, Int) -> [Complete]
complete Mode a
m ((String -> [String]) -> [String] -> [String]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> [String]
words [String]
args) (Int
argInd,Int
argPos)
            ExitCode -> IO a
forall a. ExitCode -> IO a
exitWith ExitCode
ExitSuccess
        Maybe String
Nothing -> do
            String
nam <- IO String
getProgName
            let var :: Maybe String
var = Maybe String -> Maybe String -> Maybe String
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
mplus (String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup (String
"CMDARGS_HELPER_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
forall a. Show a => a -> String
show ((Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. [a] -> a
head ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Mode a -> [String]
forall a. Mode a -> [String]
modeNames Mode a
m [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
nam])) [(String, String)]
env)
                            (String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"CMDARGS_HELPER" [(String, String)]
env)
            case Maybe String
var of
                Maybe String
Nothing -> Mode a -> [String] -> IO a
forall a. Mode a -> [String] -> IO a
processValueIO Mode a
m ([String] -> IO a) -> IO [String] -> IO a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (if Mode a -> Bool
forall a. Mode a -> Bool
modeExpandAt Mode a
m then [String] -> IO [String]
expandArgsAt else [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return) ([String] -> IO [String]) -> IO [String] -> IO [String]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO [String]
getArgs
                Just String
cmd -> do
                    Either String [String]
res <- String -> Mode a -> [String] -> IO (Either String [String])
forall a.
String -> Mode a -> [String] -> IO (Either String [String])
execute String
cmd Mode a
m []
                    case Either String [String]
res of
                        Left String
err -> do
                            Handle -> String -> IO ()
hPutStrLn Handle
stderr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Error when running helper " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
cmd
                            Handle -> String -> IO ()
hPutStrLn Handle
stderr String
err
                            IO a
forall a. IO a
exitFailure
                        Right [String]
args -> Mode a -> [String] -> IO a
forall a. Mode a -> [String] -> IO a
processValueIO Mode a
m [String]
args


readMay :: Read a => String -> Maybe a
readMay :: String -> Maybe a
readMay String
s = case [a
x | (a
x,String
t) <- ReadS a
forall a. Read a => ReadS a
reads String
s, (String
"",String
"") <- ReadS String
lex String
t] of
                [a
x] -> a -> Maybe a
forall a. a -> Maybe a
Just a
x
                [a]
_ -> Maybe a
forall a. Maybe a
Nothing


#if __GLASGOW_HASKELL__ < 800
errorWithoutStackTrace :: String -> a
errorWithoutStackTrace = error
#endif

-- | Process a list of flags (usually obtained from @'getArgs'@ and @'expandArgsAt'@) with a mode.
--   Throws an error if the command line fails to parse, or returns
--   the associated value. Implemeneted in terms of 'process'. This function
--   does not take account of any environment variables that may be set
--   (see 'processArgs').
--
--   If you are in 'IO' you will probably get a better user experience by calling 'processValueIO'.
processValue :: Mode a -> [String] -> a
processValue :: Mode a -> [String] -> a
processValue Mode a
m [String]
xs = case Mode a -> [String] -> Either String a
forall a. Mode a -> [String] -> Either String a
process Mode a
m [String]
xs of
    Left String
x -> String -> a
forall a. String -> a
errorWithoutStackTrace String
x
    Right a
x -> a
x

-- | Like 'processValue' but on failure prints to stderr and exits the program.
processValueIO :: Mode a -> [String] -> IO a
processValueIO :: Mode a -> [String] -> IO a
processValueIO Mode a
m [String]
xs = case Mode a -> [String] -> Either String a
forall a. Mode a -> [String] -> Either String a
process Mode a
m [String]
xs of
    Left String
x -> do Handle -> String -> IO ()
hPutStrLn Handle
stderr String
x; IO a
forall a. IO a
exitFailure
    Right a
x -> a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x


-- | Create a help flag triggered by @-?@/@--help@.
flagHelpSimple :: (a -> a) -> Flag a
flagHelpSimple :: (a -> a) -> Flag a
flagHelpSimple a -> a
f = [String] -> (a -> a) -> String -> Flag a
forall a. [String] -> (a -> a) -> String -> Flag a
flagNone [String
"help",String
"?"] a -> a
f String
"Display help message"


-- | Create a help flag triggered by @-?@/@--help@. The user
--   may optionally modify help by specifying the format, such as:
--
-- > --help=all          - help for all modes
-- > --help=html         - help in HTML format
-- > --help=100          - wrap the text at 100 characters
-- > --help=100,one      - full text wrapped at 100 characters
flagHelpFormat :: (HelpFormat -> TextFormat -> a -> a) -> Flag a
flagHelpFormat :: (HelpFormat -> TextFormat -> a -> a) -> Flag a
flagHelpFormat HelpFormat -> TextFormat -> a -> a
f = (String -> [String] -> Update a -> String -> String -> Flag a
forall a.
String -> [String] -> Update a -> String -> String -> Flag a
flagOpt String
"" [String
"help",String
"?"] Update a
upd String
"" String
"Display help message"){flagInfo :: FlagInfo
flagInfo = String -> FlagInfo
FlagOptRare String
""}
    where
        upd :: Update a
upd String
s a
v = case String -> Either String (HelpFormat, TextFormat)
format String
s of
            Left String
e -> String -> Either String a
forall a b. a -> Either a b
Left String
e
            Right (HelpFormat
a,TextFormat
b) -> a -> Either String a
forall a b. b -> Either a b
Right (a -> Either String a) -> a -> Either String a
forall a b. (a -> b) -> a -> b
$ HelpFormat -> TextFormat -> a -> a
f HelpFormat
a TextFormat
b a
v

        format :: String -> Either String (HelpFormat,TextFormat)
        format :: String -> Either String (HelpFormat, TextFormat)
format String
xs = (Either String (HelpFormat, TextFormat)
 -> String -> Either String (HelpFormat, TextFormat))
-> Either String (HelpFormat, TextFormat)
-> [String]
-> Either String (HelpFormat, TextFormat)
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\Either String (HelpFormat, TextFormat)
acc String
x -> (String -> Either String (HelpFormat, TextFormat))
-> ((HelpFormat, TextFormat)
    -> Either String (HelpFormat, TextFormat))
-> Either String (HelpFormat, TextFormat)
-> Either String (HelpFormat, TextFormat)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> Either String (HelpFormat, TextFormat)
forall a b. a -> Either a b
Left (String
-> (HelpFormat, TextFormat)
-> Either String (HelpFormat, TextFormat)
f String
x) Either String (HelpFormat, TextFormat)
acc) ((HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat, TextFormat)
forall a. Default a => a
def) (String -> [String]
sep String
xs)
            where
                sep :: String -> [String]
sep = String -> [String]
words (String -> [String]) -> (String -> String) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map (\Char
x -> if Char
x Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
":," then Char
' ' else Char -> Char
toLower Char
x)
                f :: String
-> (HelpFormat, TextFormat)
-> Either String (HelpFormat, TextFormat)
f String
x (HelpFormat
a,TextFormat
b) = case String
x of
                    String
"all" -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
HelpFormatAll,TextFormat
b)
                    String
"one" -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
HelpFormatOne,TextFormat
b)
                    String
"def" -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
HelpFormatDefault,TextFormat
b)
                    String
"html" -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
a,TextFormat
HTML)
                    String
"text" -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
a,TextFormat
defaultWrap)
                    String
"bash" -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
HelpFormatBash,Int -> TextFormat
Wrap Int
1000000)
                    String
"zsh"  -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
HelpFormatZsh ,Int -> TextFormat
Wrap Int
1000000)
                    String
_ | (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
x -> (HelpFormat, TextFormat) -> Either String (HelpFormat, TextFormat)
forall a b. b -> Either a b
Right (HelpFormat
a,Int -> TextFormat
Wrap (Int -> TextFormat) -> Int -> TextFormat
forall a b. (a -> b) -> a -> b
$ String -> Int
forall a. Read a => String -> a
read String
x)
                    String
_ -> String -> Either String (HelpFormat, TextFormat)
forall a b. a -> Either a b
Left String
"unrecognised help format, expected one of: all one def html text <NUMBER>"


-- | Create a version flag triggered by @-V@/@--version@.
flagVersion :: (a -> a) -> Flag a
flagVersion :: (a -> a) -> Flag a
flagVersion a -> a
f = [String] -> (a -> a) -> String -> Flag a
forall a. [String] -> (a -> a) -> String -> Flag a
flagNone [String
"version",String
"V"] a -> a
f String
"Print version information"

-- | Create a version flag triggered by @--numeric-version@.
flagNumericVersion :: (a -> a) -> Flag a
flagNumericVersion :: (a -> a) -> Flag a
flagNumericVersion a -> a
f = [String] -> (a -> a) -> String -> Flag a
forall a. [String] -> (a -> a) -> String -> Flag a
flagNone [String
"numeric-version"] a -> a
f String
"Print just the version number"


-- | Create verbosity flags triggered by @-v@/@--verbose@ and
--   @-q@/@--quiet@
flagsVerbosity :: (Verbosity -> a -> a) -> [Flag a]
flagsVerbosity :: (Verbosity -> a -> a) -> [Flag a]
flagsVerbosity Verbosity -> a -> a
f =
    [[String] -> (a -> a) -> String -> Flag a
forall a. [String] -> (a -> a) -> String -> Flag a
flagNone [String
"verbose",String
"v"] (Verbosity -> a -> a
f Verbosity
Loud) String
"Loud verbosity"
    ,[String] -> (a -> a) -> String -> Flag a
forall a. [String] -> (a -> a) -> String -> Flag a
flagNone [String
"quiet",String
"q"] (Verbosity -> a -> a
f Verbosity
Quiet) String
"Quiet verbosity"]