{-# 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 :: forall a. Data a => a -> IO a cmdArgs = forall a. Mode (CmdArgs a) -> IO a cmdArgsRun forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. Data a => a -> Mode (CmdArgs a) 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_ :: forall a. Data a => Annotate Ann -> IO a cmdArgs_ = forall a. Mode (CmdArgs a) -> IO a cmdArgsRun forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. Data a => Annotate Ann -> Mode (CmdArgs a) cmdArgsMode_ cmdArgsCapture :: Data a => Capture Ann -> Mode (CmdArgs a) cmdArgsCapture :: forall a. Data a => Capture Ann -> Mode (CmdArgs a) cmdArgsCapture = forall (m :: * -> *) a b. Remap m => (a -> b) -> (b -> (a, a -> b)) -> m a -> m b remap CmdArgs Any -> CmdArgs a embed forall {f :: * -> *} {a}. (Functor f, Data a) => f a -> (f Any, CmdArgs Any -> CmdArgs a) proj forall b c a. (b -> c) -> (a -> b) -> a -> c . Prog_ -> Mode (CmdArgs Any) global forall b c a. (b -> c) -> (a -> b) -> a -> c . Capture Ann -> Prog_ local where embed :: CmdArgs Any -> CmdArgs a embed = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap forall a. Typeable a => Any -> a fromAny proj :: f a -> (f Any, CmdArgs Any -> CmdArgs a) proj f a x = (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap forall a. Data a => a -> Any Any f a x, CmdArgs Any -> CmdArgs a 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 :: forall a. Data a => a -> Mode (CmdArgs a) cmdArgsMode = forall a. Data a => Capture Ann -> Mode (CmdArgs a) cmdArgsCapture forall b c a. (b -> c) -> (a -> b) -> a -> c . forall val ann. (Data val, Data ann) => val -> Capture ann 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_ :: forall a. Data a => Annotate Ann -> Mode (CmdArgs a) cmdArgsMode_ = forall a. Data a => Capture Ann -> Mode (CmdArgs a) cmdArgsCapture forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. Show a => Annotate a -> Capture a 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 :: forall a. Mode (CmdArgs a) -> IO a cmdArgsRun Mode (CmdArgs a) m = forall a. CmdArgs a -> IO a cmdArgsApply forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b =<< forall a. Mode a -> IO a processArgs Mode (CmdArgs a) 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 :: forall a. CmdArgs a -> IO a cmdArgsApply CmdArgs{a Maybe String Maybe Verbosity CmdArgsPrivate cmdArgsPrivate :: forall a. CmdArgs a -> CmdArgsPrivate cmdArgsVerbosity :: forall a. CmdArgs a -> Maybe Verbosity cmdArgsVersion :: forall a. CmdArgs a -> Maybe String cmdArgsHelp :: forall a. CmdArgs a -> Maybe String cmdArgsValue :: forall a. CmdArgs a -> a cmdArgsPrivate :: CmdArgsPrivate cmdArgsVerbosity :: Maybe Verbosity cmdArgsVersion :: Maybe String cmdArgsHelp :: Maybe String cmdArgsValue :: a ..} | Just String x <- Maybe String cmdArgsHelp = do String -> IO () putStr String x; forall a. IO a exitSuccess | Just String x <- Maybe String cmdArgsVersion = do String -> IO () putStr String x; forall a. IO a exitSuccess | Bool otherwise = do forall b a. b -> (a -> b) -> Maybe a -> b maybe (forall (m :: * -> *) a. Monad m => a -> m a return ()) Verbosity -> IO () setVerbosity Maybe Verbosity cmdArgsVerbosity forall (m :: * -> *) a. Monad m => a -> m a return a 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 :: forall a. Mode (CmdArgs a) -> CmdArgs a -> [String] _cmdArgsReform Mode (CmdArgs a) m CmdArgs a x = forall a. a -> Maybe a -> a fromMaybe (forall a. HasCallStack => String -> a error String err) forall a b. (a -> b) -> a -> b $ forall a. Mode a -> a -> Maybe [String] modeReform Mode (CmdArgs a) m CmdArgs a x where err :: String err = String "System.Console.CmdArgs.Implicit.cmdArgsReform: cannot reform the arguments, perhaps the mode was not " forall a. [a] -> [a] -> [a] ++ String "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 :: forall val. Data val => [val] -> val modes = forall val. Data val => [val] -> val 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 :: forall val. Data val => [val] -> val enum = forall val. Data val => [val] -> val 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 &= :: forall val. Data val => val -> Ann -> val (&=) = forall val ann. (Data val, Data ann) => val -> ann -> val (A.&=) -- | Like 'enum', but using the pure annotations. enum_ :: (Data c, Data f) => (c -> f) -> [Annotate Ann] -> Annotate Ann enum_ :: forall c f. (Data c, Data f) => (c -> f) -> [Annotate Ann] -> Annotate Ann enum_ = forall ann c f. (Data c, Data f) => (c -> f) -> [Annotate ann] -> Annotate ann (:=+) -- | Like 'modes', but using the pure annotations. modes_ :: [Annotate Ann] -> Annotate Ann modes_ :: [Annotate Ann] -> Annotate Ann modes_ = forall a. [Annotate a] -> Annotate a many_