1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Biz.Cli
( Plan (..),
main,
Docopt.Docopt (..),
Docopt.Arguments,
Docopt.argument,
Docopt.docopt,
Docopt.getAllArgs,
Docopt.getArg,
Docopt.getArgWithDefault,
Docopt.longOption,
Docopt.shortOption,
Docopt.command,
has,
)
where
import Alpha
import qualified Biz.Test as Test
import qualified System.Console.Docopt as Docopt
import qualified System.Environment as Environment
-- | Plan is the main data structure that describes a CLI program. It's not the
-- best name, but it works. This type is parameterized with `cfg` so you can
-- load configuration from the environment and pass it into your Plan.
data Plan cfg = Plan
{ -- | Usage info, shows when given --help
help :: Docopt.Docopt,
-- | The main function takes arguments and produces effects. Maybe it should
-- also take `cfg` as an argument?
move :: Docopt.Arguments -> IO (),
-- | The test suite for the gram, invoked when 'test' is passed as the first
-- argument to the program
test :: Test.Tree,
-- | Function for cleaning up any files or resources, presumably on
-- shutdown. Can be just `pure` if you have nothing to tidy.
tidy :: cfg -> IO ()
}
-- | The entrypoint for CLI programs, use this in your own `main`.
main :: Plan cfg -> IO ()
main Plan {..} =
Environment.getArgs
/> Docopt.parseArgs help
+> \case
Left err -> panic <| show err
Right args ->
if args `has` Docopt.command "test"
then Test.run test
else
if args `has` Docopt.longOption "help" || args `has` Docopt.shortOption 'h'
then Docopt.exitWithUsage help
else move args
has :: Docopt.Arguments -> Docopt.Option -> Bool
has = Docopt.isPresent
|