cmdargs-0.10.21: System/Console/CmdArgs/Implicit.hs
{-# LANGUAGE RecordWildCards, PatternGuards #-}
{-|
This module provides simple command line argument processing.
The main function of interest is 'cmdArgs'.
A simple example is:
@data Sample = Sample {hello :: String} deriving (Show, Data, Typeable)@
@
sample = Sample{hello = 'def' '&=' 'help' \"World argument\" '&=' 'opt' \"world\"}
'&=' 'summary' \"Sample v1\"
@
@main = print =<< 'cmdArgs' sample@
Attributes are used to control a number of behaviours:
* The help message: 'help', 'typ', 'details', 'summary', 'program', 'groupname'
* Flag behaviour: 'opt', 'enum', 'verbosity', 'ignore'
* Flag name assignment: 'name', 'explicit'
* Controlling non-flag arguments: 'args', 'argPos'
* multi-mode programs: 'modes', 'auto'
/Supported Types/: Each field in the record must be one of the supported
atomic types (@String@, @Int@, @Integer@, @Float@, @Double@, @Bool@, an
enumeration, a tuple of atomic types) or a list (@[]@) or @Maybe@ wrapping
at atomic type.
/Missing Fields/: If a field is shared by multiple modes, it may be omitted
in subsequent modes, and will default to the previous value.
/Purity/: Values created with annotations are not pure - the first
time they are computed they will include the annotations, but subsequently
they will not. If you wish to run the above example in a more robust way:
@sample = 'cmdArgsMode' $ Sample{hello = ... -- as before@
@main = print =<< 'cmdArgsRun' sample@
Even using this scheme, sometimes GHC's optimisations may share values who
have the same annotation. To disable sharing you may need to specify
@\{\-\# OPTIONS_GHC -fno-cse \#\-\}@ in the module you define the flags.
/Pure annotations/: Alternatively, you may use pure annotations, which are
referentially transparent, but less type safe and more verbose. The initial
example may be written as:
@sample = 'record' Sample{} [hello := 'def' '+=' 'help' \"World argument\" '+=' 'opt' \"world\"]@
@ '+=' 'summary' \"Sample v1\"@
@main = print =<< (cmdArgs_ sample :: IO Sample)@
All the examples are written using impure annotations. To convert to pure
annotations follow the rules:
> Ctor {field1 = value1 &= ann1, field2 = value2} &= ann2 ==> record Ctor{} [field1 := value1 += ann1, field2 := value2] += ann2
> Ctor (value1 &= ann1) value2 &= ann2 ==> record Ctor{} [atom value1 += ann1, atom value2] += ann2
> modes [Ctor1{...}, Ctor2{...}] ==> modes_ [record Ctor1{} [...], record Ctor2{} [...]]
> Ctor {field1 = enum [X &= ann, Y]} ==> record Ctor{} [enum_ field1 [atom X += ann, atom Y]]
If you are willing to use TemplateHaskell, you can write in the impure syntax,
but have your code automatically translated to the pure style. For more details see
"System.Console.CmdArgs.Quote".
-}
module System.Console.CmdArgs.Implicit(
-- * Running command lines
cmdArgs, cmdArgsMode, cmdArgsRun, cmdArgs_, cmdArgsMode_, cmdArgsApply, CmdArgs(..), -- cmdArgsReform,
-- * Constructing command lines
-- | Attributes can work on a flag (inside a field), on a mode (outside the record),
-- or on all modes (outside the 'modes' call).
module System.Console.CmdArgs.Implicit.UI,
-- ** Impure
(&=), modes, enum,
-- ** Pure
(+=), record, atom, Annotate((:=)), enum_, modes_,
-- * Re-exported for convenience
-- | Provides a few opaque types (for writing type signatures),
-- verbosity control, default values with 'def' and the
-- @Data@/@Typeable@ type classes.
module System.Console.CmdArgs.Verbosity,
module System.Console.CmdArgs.Default,
Ann, Mode,
Data, Typeable
) where
import Data.Data
import Data.Maybe
import Data.Generics.Any
import System.Exit
import System.Console.CmdArgs.Explicit(Mode,processArgs,remap,modeReform)
import System.Console.CmdArgs.Implicit.Ann
import System.Console.CmdArgs.Annotate hiding ((&=))
import qualified System.Console.CmdArgs.Annotate as A((&=))
import System.Console.CmdArgs.Implicit.Type
import System.Console.CmdArgs.Implicit.Local
import System.Console.CmdArgs.Implicit.Global
import System.Console.CmdArgs.Implicit.UI
import System.Console.CmdArgs.Verbosity
import System.Console.CmdArgs.Default
-- | Take impurely annotated records and run the corresponding command line.
-- Shortcut for @'cmdArgsRun' . 'cmdArgsMode'@.
--
-- To use 'cmdArgs' with custom command line arguments see
-- 'System.Environment.withArgs'.
cmdArgs :: Data a => a -> IO a
cmdArgs = cmdArgsRun . cmdArgsMode
-- | Take purely annotated records and run the corresponding command line.
-- Shortcut for @'cmdArgsRun' . 'cmdArgsMode_'@.
--
-- To use 'cmdArgs_' with custom command line arguments see
-- 'System.Environment.withArgs'.
cmdArgs_ :: Data a => Annotate Ann -> IO a
cmdArgs_ = cmdArgsRun . cmdArgsMode_
cmdArgsCapture :: Data a => Capture Ann -> Mode (CmdArgs a)
cmdArgsCapture = remap embed proj . global . local
where embed = fmap fromAny
proj x = (fmap Any x, embed)
-- | Take impurely annotated records and turn them in to a 'Mode' value, that can
-- make use of the "System.Console.CmdArgs.Explicit" functions (i.e. 'process').
--
-- Annotated records are impure, and will only contain annotations on
-- their first use. The result of this function is pure, and can be reused.
cmdArgsMode :: Data a => a -> Mode (CmdArgs a)
cmdArgsMode = cmdArgsCapture . capture
-- | Take purely annotated records and turn them in to a 'Mode' value, that can
-- make use of the "System.Console.CmdArgs.Explicit" functions (i.e. 'process').
cmdArgsMode_ :: Data a => Annotate Ann -> Mode (CmdArgs a)
cmdArgsMode_ = cmdArgsCapture . capture_
-- | Run a Mode structure. This function reads the command line arguments
-- and then performs as follows:
--
-- * If invalid arguments are given, it will display the error message
-- and exit.
--
-- * If @--help@ is given, it will display the help message and exit.
--
-- * If @--version@ is given, it will display the version and exit.
--
-- * In all other circumstances the program will return a value.
--
-- * Additionally, if either @--quiet@ or @--verbose@ is given (see 'verbosity')
-- it will set the verbosity (see 'setVerbosity').
cmdArgsRun :: Mode (CmdArgs a) -> IO a
cmdArgsRun m = cmdArgsApply =<< processArgs m
-- | Perform the necessary actions dictated by a 'CmdArgs' structure.
--
-- * If 'cmdArgsHelp' is @Just@, it will display the help message and exit.
--
-- * If 'cmdArgsVersion' is @Just@, it will display the version and exit.
--
-- * In all other circumstances it will return a value.
--
-- * Additionally, if 'cmdArgsVerbosity' is @Just@ (see 'verbosity')
-- it will set the verbosity (see 'setVerbosity').
cmdArgsApply :: CmdArgs a -> IO a
cmdArgsApply CmdArgs{..}
| Just x <- cmdArgsHelp = do putStr x; exitSuccess
| Just x <- cmdArgsVersion = do putStr x; exitSuccess
| otherwise = do
maybe (return ()) setVerbosity cmdArgsVerbosity
return cmdArgsValue
-- | Produce command line arguments that would generate the given value. This
-- function is useful for taking a value resulting from a command line,
-- modifying it (perhaps changing the value of a flag) and generating fresh
-- command line arguments.
--
-- > forall mode values constructed by cmdArgsMode/cmdArgsMode:
-- > forall args which successfully parse with mode
-- > let x = processValue mode args
-- > processValue mode (cmdArgsReform mode $ fromRight x) == x
_cmdArgsReform :: Mode (CmdArgs a) -> CmdArgs a -> [String]
_cmdArgsReform m x = fromMaybe (error err) $ modeReform m x
where err = "System.Console.CmdArgs.Implicit.cmdArgsReform: cannot reform the arguments, perhaps the mode was not " ++
"generated by cmdArgsMode/cmdArgsMode_ ?"
-- | Modes: \"I want a program with multiple modes, like darcs or cabal.\"
--
-- Takes a list of modes, and creates a mode which includes them all.
-- If you want one of the modes to be chosen by default, see 'auto'.
--
-- > data Modes = Mode1 | Mode2 | Mode3 deriving Data
-- > cmdArgs $ modes [Mode1,Mode2,Mode3]
modes :: Data val => [val] -> val
modes = many
-- | Flag: \"I want several different flags to set this one field to different values.\"
--
-- This annotation takes a type which is an enumeration, and provides multiple
-- separate flags to set the field to each value. The first element in the list
-- is used as the value of the field.
--
-- > data State = On | Off deriving Data
-- > data Mode = Mode {state :: State}
-- > cmdArgs $ Mode {state = enum [On &= help "Turn on",Off &= help "Turn off"]}
-- > --on Turn on
-- > --off Turn off
--
-- This annotation can be used to allow multiple flags within a field:
--
-- > data Mode = Mode {state :: [State]}
-- > cmdArgs $ Mode {state = enum [[] &= ignore, [On] &= help "Turn on", [Off] &= help "Turn off"]}
--
-- Now @--on --off@ would produce @Mode [On,Off]@.
enum :: Data val => [val] -> val
enum = many
-- | Add an annotation to a value. Note that if the value is evaluated
-- more than once the annotation will only be available the first time.
{-# INLINE (&=) #-}
(&=) :: Data val => val -> Ann -> val
(&=) = (A.&=)
-- | Like 'enum', but using the pure annotations.
enum_ :: (Data c, Data f) => (c -> f) -> [Annotate Ann] -> Annotate Ann
enum_ = (:=+)
-- | Like 'modes', but using the pure annotations.
modes_ :: [Annotate Ann] -> Annotate Ann
modes_ = many_