File: Main.hs

package info (click to toggle)
haskell-optparse-simple 0.1.1.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 80 kB
  • sloc: haskell: 210; makefile: 4
file content (142 lines) | stat: -rw-r--r-- 3,231 bytes parent folder | download | duplicates (4)
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}

import Options.Applicative.Simple hiding(action)
import GHC.IO.Handle
import System.IO
import System.Environment
import Control.Exception
import Control.Monad
import System.Directory
import System.Exit
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Monoid ((<>))


shouldBe :: (Show a, Eq a) => a -> a -> IO ()
shouldBe actual expected
  | expected == actual = return ()
  | otherwise = do
    putStrLn $ "expected: " ++ show expected
    putStrLn $ "actual  : " ++ show actual
    exitFailure

catchReturn :: Exception e => IO e -> IO e
catchReturn io = io `catch` return

catchExitCode :: IO () -> IO ExitCode
catchExitCode action = catchReturn $ do
  action
  return ExitSuccess

data FakeHandles = FakeHandles
  { fakeIn  :: Handle
  , fakeOut :: Handle
  , fakeErr :: Handle
  , realIn  :: Handle
  , realOut :: Handle
  , realErr :: Handle
  }

openFile' :: FilePath -> IO Handle
openFile' path = do
  removeIfExists path
  openFile path ReadWriteMode

removeIfExists :: FilePath -> IO ()
removeIfExists path = do
  exists <- doesFileExist path
  when exists $ do
    removeFile path

stdinFile :: FilePath
stdinFile = ".tmp.stdin"

stdoutFile :: FilePath
stdoutFile = ".tmp.stdout"

stderrFile :: FilePath
stderrFile = ".tmp.stderr"

beforeFH :: IO FakeHandles
beforeFH = do
  realIn <- hDuplicate stdin
  realOut <- hDuplicate stdout
  realErr <- hDuplicate stderr

  fakeIn <- openFile stdinFile ReadWriteMode
  fakeOut <- openFile' stdoutFile
  fakeErr <- openFile' stderrFile

  hDuplicateTo fakeIn stdin
  hDuplicateTo fakeOut stdout
  hDuplicateTo fakeErr stderr

  return FakeHandles{..}

afterFH :: FakeHandles -> IO ()
afterFH FakeHandles{..} = do
  hDuplicateTo realIn stdin
  hDuplicateTo realOut stdout
  hDuplicateTo realErr stderr

  hClose fakeIn
  hClose fakeOut
  hClose fakeErr

withFakeHandles :: IO a -> IO a
withFakeHandles = bracket beforeFH afterFH . const

withStdIn :: ByteString -> IO ()
  -> IO (ByteString, ByteString, ExitCode)
withStdIn inBS action = do
  BS.writeFile stdinFile inBS
  withFakeHandles $ do
    _ <- catchExitCode action
    hFlush stdout
    hFlush stderr
  out <- BS.readFile stdoutFile
  err <- BS.readFile stderrFile

  removeIfExists stdinFile
  removeIfExists stdoutFile
  removeIfExists stderrFile

  return (out, err, ExitSuccess)


main :: IO ()
main = do
  (out, err, exitCode) <- withStdIn ""
    $ withArgs ["--version"]
    $ simpleProg
  exitCode `shouldBe` ExitSuccess
  err `shouldBe` ""
  out `shouldBe` "version\n"

  (out', err', exitCode') <- withStdIn ""
    $ withArgs ["--summary"]
    $ summaryProg
  exitCode' `shouldBe` ExitSuccess
  err' `shouldBe` ""
  out' `shouldBe` "A program summary\n"

  return ()


simpleProg :: IO ()
simpleProg = do
  ((), ()) <- simpleOptions "version" "header" "desc" (pure ()) empty
  return ()

parserWithSummary :: Parser ()
parserWithSummary = summaryOption <*> pure () where
  summaryOption = infoOption "A program summary"
    $ long "summary"
   <> help "Show program summary"

summaryProg :: IO ()
summaryProg = do
  ((), ()) <- simpleOptions "version" "header" "desc" parserWithSummary empty
  return ()