getopt-generics-0.11.0.3: src/System/Console/GetOpt/Generics.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
module System.Console.GetOpt.Generics (
-- * Simple IO API
withCli,
WithCli(),
HasArguments,
WithCli.Argument(argumentType, parseArgument),
-- * Customizing the CLI
withCliModified,
Modifier(..),
-- * IO API
getArguments,
modifiedGetArguments,
-- * Pure API
parseArguments,
Result(..),
-- * Re-exports from "Generics.SOP"
Generics.SOP.Generic,
HasDatatypeInfo,
Code,
All2,
) where
import Generics.SOP
import System.Environment
import WithCli
import WithCli.Parser
import WithCli.HasArguments
import WithCli.Result
import System.Console.GetOpt.Generics.Modifier
-- | Parses command line arguments (gotten from 'withArgs') and returns the
-- parsed value. This function should be enough for simple use-cases.
--
-- Throws the same exceptions as 'withCli'.
--
-- Here's an example:
-- ### Start "docs/RecordType.hs" "" Haddock ###
-- |
-- > {-# LANGUAGE DeriveGeneric #-}
-- >
-- > module RecordType where
-- >
-- > import qualified GHC.Generics
-- > import System.Console.GetOpt.Generics
-- >
-- > -- All you have to do is to define a type and derive some instances:
-- >
-- > data Options
-- > = Options {
-- > port :: Int,
-- > daemonize :: Bool,
-- > config :: Maybe FilePath
-- > }
-- > deriving (Show, GHC.Generics.Generic)
-- >
-- > instance Generic Options
-- > instance HasDatatypeInfo Options
-- >
-- > -- Then you can use `getArguments` to create a command-line argument parser:
-- >
-- > main :: IO ()
-- > main = do
-- > options <- getArguments
-- > print (options :: Options)
-- ### End ###
-- | And this is how the above program behaves:
-- ### Start "docs/RecordType.shell-protocol" "" Haddock ###
-- |
-- > $ program --port 8080 --config some/path
-- > Options {port = 8080, daemonize = False, config = Just "some/path"}
-- > $ program --port 8080 --daemonize
-- > Options {port = 8080, daemonize = True, config = Nothing}
-- > $ program --port foo
-- > cannot parse as INTEGER: foo
-- > # exit-code 1
-- > $ program
-- > missing option: --port=INTEGER
-- > # exit-code 1
-- > $ program --help
-- > program [OPTIONS]
-- > --port=INTEGER
-- > --daemonize
-- > --config=STRING (optional)
-- > -h --help show help and exit
-- ### End ###
getArguments :: forall a . (Generic a, HasDatatypeInfo a, All2 HasArguments (Code a)) =>
IO a
getArguments = modifiedGetArguments []
-- | Like 'getArguments` but allows you to pass in 'Modifier's.
modifiedGetArguments :: forall a . (Generic a, HasDatatypeInfo a, All2 HasArguments (Code a)) =>
[Modifier] -> IO a
modifiedGetArguments modifiers = do
args <- getArgs
progName <- getProgName
handleResult $ parseArguments progName modifiers args
-- | Pure variant of 'modifiedGetArguments'.
--
-- Does not throw any exceptions.
parseArguments :: forall a . (Generic a, HasDatatypeInfo a, All2 HasArguments (Code a)) =>
String -- ^ Name of the program (e.g. from 'getProgName').
-> [Modifier] -- ^ List of 'Modifier's to manually tweak the command line interface.
-> [String] -- ^ List of command line arguments to parse (e.g. from 'getArgs').
-> Result a
parseArguments progName mods args = do
modifiers <- mkModifiers mods
parser <- genericParser modifiers
runParser progName modifiers
(normalizeParser (applyModifiers modifiers parser)) args