Safe Haskell | None |
---|---|
Language | Haskell2010 |
- withCli :: WithCli main => main -> IO ()
- class WithCli main
- class HasArguments a
- class Argument a where
- argumentType :: Proxy a -> String
- parseArgument :: String -> Maybe a
- withCliModified :: WithCli main => [Modifier] -> main -> IO ()
- data Modifier
- getArguments :: forall a. (Generic a, GTo a, GDatatypeInfo a, All2 HasArguments (GCode a)) => IO a
- modifiedGetArguments :: forall a. (Generic a, GTo a, GDatatypeInfo a, All2 HasArguments (GCode a)) => [Modifier] -> IO a
- parseArguments :: forall a. (Generic a, GTo a, GDatatypeInfo a, All2 HasArguments (GCode a)) => String -> [Modifier] -> [String] -> Result a
- data Result a
- = Success a
- | Errors [String]
- | OutputAndExit String
- class Generic a
- type GDatatypeInfo a = GDatatypeInfo' (Rep a)
- type GCode a = ToSumCode (Rep a) ([] [*])
- class (AllF [k] (All k f) xss, SListI [k] xss) => All2 f xss
Simple IO API
withCli :: WithCli main => main -> IO () Source
withCli
converts an IO operation into a program with a proper CLI.
Retrieves command line arguments through withArgs
.
main
(the given IO operation) can have arbitrarily many parameters
provided all parameters have instances for HasArguments
.
May throw the following exceptions:
in case of invalid options. Error messages are written toExitFailure
1stderr
.
in caseExitSuccess
--help
is given. (
behaves like a normal exception, except that -- if uncaught -- the process will exit with exit-codeExitSuccess
0
.) Help output is written tostdout
.
Example:
import WithCli main :: IO () main = withCli run run :: String -> Int -> Bool -> IO () run s i b = print (s, i, b)
Using the above program in a shell:
$ program foo 42 true ("foo",42,True) $ program --help program [OPTIONS] STRING INTEGER BOOL -h --help show help and exit $ program foo 42 bar cannot parse as BOOL: bar # exit-code 1 $ program missing argument of type STRING missing argument of type INTEGER missing argument of type BOOL # exit-code 1 $ program foo 42 yes bar unknown argument: bar # exit-code 1
class HasArguments a Source
Everything that can be used as an argument to your main
function
(see withCli
) needs to have a HasArguments
instance.
HasArguments
also allows to conjure up instances for record types
to create more complex command line interfaces. Here's an example:
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} import WithCli data Options = Options { port :: Int, daemonize :: Bool, config :: Maybe FilePath } deriving (Show, Generic, HasArguments) main :: IO () main = withCli run run :: Options -> IO () run = print
In a shell this program behaves like this:
$ 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
HasArguments Bool Source | |
HasArguments Double Source | |
HasArguments Float Source | |
HasArguments Int Source | |
HasArguments String Source | |
Argument a => HasArguments [a] Source | |
Argument a => HasArguments (Maybe a) Source | |
(HasArguments a, HasArguments b) => HasArguments (a, b) Source | |
(HasArguments a, HasArguments b, HasArguments c) => HasArguments (a, b, c) Source |
Argument
is a typeclass for things that can be parsed as atomic values from
single command line arguments, e.g. strings (and filenames) and numbers.
Occasionally you might want to declare your own instance for additional type safety and for providing a more informative command argument type. Here's an example:
{-# LANGUAGE DeriveDataTypeable #-} import WithCli data File = File FilePath deriving (Show, Typeable) instance Argument File where argumentType Proxy = "custom-file-type" parseArgument f = Just (File f) instance HasArguments File where argumentsParser = atomicArgumentsParser main :: IO () main = withCli run run :: File -> IO () run = print
And this is how the above program behaves:
$ program --help program [OPTIONS] custom-file-type -h --help show help and exit $ program some/file File "some/file"
argumentType :: Proxy a -> String Source
parseArgument :: String -> Maybe a Source
Customizing the CLI
withCliModified :: WithCli main => [Modifier] -> main -> IO () Source
Modifier
s can be used to customize the command line parser.
AddShortOption String Char |
|
RenameOption String String |
|
RenameOptions (String -> Maybe String) |
Can be used together with |
UseForPositionalArguments String String |
|
AddOptionHelp String String |
|
AddVersionFlag String |
|
IO API
getArguments :: forall a. (Generic a, GTo a, GDatatypeInfo a, All2 HasArguments (GCode a)) => IO a Source
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:
{-# LANGUAGE DeriveGeneric #-} module RecordType where import System.Console.GetOpt.Generics -- All you have to do is to define a type and derive an instance for Generic: data Options = Options { port :: Int, daemonize :: Bool, config :: Maybe FilePath } deriving (Show, Generic) -- Then you can use `getArguments` to create a command-line argument parser: main :: IO () main = do options <- getArguments print (options :: Options)
And this is how the above program behaves:
$ 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
modifiedGetArguments :: forall a. (Generic a, GTo a, GDatatypeInfo a, All2 HasArguments (GCode a)) => [Modifier] -> IO a Source
Like getArguments
but allows you to pass in Modifier
s.
Pure API
:: (Generic a, GTo a, GDatatypeInfo a, All2 HasArguments (GCode a)) | |
=> String | Name of the program (e.g. from |
-> [Modifier] | List of |
-> [String] | List of command line arguments to parse (e.g. from |
-> Result a |
Pure variant of modifiedGetArguments
.
Does not throw any exceptions.
Type to wrap results from the pure parsing functions.
Success a | The CLI was used correctly and a value of type |
Errors [String] | The CLI was used incorrectly. The It can also happen that the data type you're trying to use isn't supported. See the README for details. |
OutputAndExit String | The CLI was used with |
Re-exports
class Generic a
Representable types of kind *. This class is derivable in GHC with the DeriveGeneric flag on.
Generic Bool | |
Generic Char | |
Generic Double | |
Generic Float | |
Generic Int | |
Generic Ordering | |
Generic () | |
Generic All | |
Generic Any | |
Generic Arity | |
Generic Fixity | |
Generic Associativity | |
Generic [a] | |
Generic (U1 p) | |
Generic (Par1 p) | |
Generic (ZipList a) | |
Generic (Dual a) | |
Generic (Endo a) | |
Generic (Sum a) | |
Generic (Product a) | |
Generic (First a) | |
Generic (Last a) | |
Generic (Maybe a) | |
Generic (I a) | |
Generic (Either a b) | |
Generic (Rec1 f p) | |
Generic (a, b) | |
Generic (Const a b) | |
Generic (WrappedMonad m a) | |
Generic (Proxy * t) | |
Generic (K1 i c p) | |
Generic ((:+:) f g p) | |
Generic ((:*:) f g p) | |
Generic ((:.:) f g p) | |
Generic (a, b, c) | |
Generic (WrappedArrow a b c) | |
Generic (Alt k f a) | |
Generic (K k a b) | |
Generic (M1 i c f p) | |
Generic (a, b, c, d) | |
Generic (a, b, c, d, e) | |
Generic ((:.:) l k f g p) | |
Generic (a, b, c, d, e, f) | |
Generic (a, b, c, d, e, f, g) |
type GDatatypeInfo a = GDatatypeInfo' (Rep a)
Constraint for the class that computes gdatatypeInfo
.
type GCode a = ToSumCode (Rep a) ([] [*])
Compute the SOP code of a datatype.
This requires that Rep
is defined, which in turn requires that
the type has a Generic
(from module GHC.Generics) instance.
This is the default definition for Code
.
For more info, see Generic
.
class (AllF [k] (All k f) xss, SListI [k] xss) => All2 f xss
Require a constraint for every element of a list of lists.
If you have a datatype that is indexed over a type-level
list of lists, then you can use All2
to indicate that all
elements of the innert lists must satisfy a given constraint.
Example: The constraint
All2 Eq '[ '[ Int ], '[ Bool, Char ] ]
is equivalent to the constraint
(Eq Int, Eq Bool, Eq Char)
Example: A type signature such as
f :: All2 Eq xss => SOP I xs -> ...
means that f
can assume that all elements of the sum
of product satisfy Eq
.