on master help sign in
Merge pull request #197 from andreasabel/optparse-applicative
2ba7d84Michael Snoyman
yaml/exe/yaml2json.hs+85−20
@@ -1,31 +1,96 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE NamedFieldPuns #-}
4+
1import Prelude hiding (putStr, getContents)
5import Prelude hiding (putStr, getContents)
2
6
3−import Data.Aeson (encode, Value)
7+import Data.Aeson (encode, Value)
4−import Data.ByteString (getContents)
8+import Data.ByteString (getContents)
5import Data.ByteString.Lazy (putStr)
9import Data.ByteString.Lazy (putStr)
6−import System.Environment (getArgs)
10+import Data.List (intercalate)
11+#if !MIN_VERSION_base(4,11,0)
12+import Data.Semigroup (Semigroup(..))
13+#endif
14+import Data.Version (Version(versionBranch))
15+import Data.Yaml (decodeFileEither, decodeEither')
16+
17+import Options.Applicative
18+ ( Parser
19+ , action, execParser, header, help, helper, hidden
20+ , info, infoOption, long, metavar, short, strArgument
21+ )
22+
7import System.Exit
23import System.Exit
8−import System.IO (stderr, hPutStrLn)
24+import System.IO (stderr, hPrint)
25+
26+import qualified Paths_yaml as Paths
27+
28+newtype Options = Options
29+ { optInput :: Maybe FilePath
30+ -- ^ Nothing means @stdin@.
31+ }
32+
33+-- | Name of the executable.
34+
35+self :: String
36+self = "yaml2json"
37+
38+-- | Homepage for this program.
9
39
10−import Data.Yaml (decodeFileEither, decodeEither')
40+homepage :: String
41+homepage = "https://github.com/snoyberg/yaml/"
11
42
12−helpMessage :: IO ()
43+-- | Version in @x.y.z@ format.
13−helpMessage = putStrLn "Usage: yaml2json FILE\n\nuse '-' as FILE to indicate stdin" >> exitFailure
44+
45+version :: String
46+version = intercalate "." $ map show $ versionBranch Paths.version
47+
48+-- | Parse options; handle parse errors, @--help@, @--version@.
49+
50+options :: IO Options
51+options =
52+ execParser $
53+ info (helper <*> versionOption <*> numericVersionOption <*> programOptions)
54+ (header "Reads given YAML document and prints it JSON format to standard out.")
55+
56+ where
57+ versionOption =
58+ infoOption (unwords versionWords)
59+ $ long "version"
60+ <> short 'V'
61+ <> hidden
62+ <> help "Show version info."
63+ versionWords = [ self, "version", version, homepage ]
64+
65+ numericVersionOption =
66+ infoOption version
67+ $ long "numeric-version"
68+ <> hidden
69+ <> help "Show just version number."
70+
71+ programOptions = Options <$> oInput
72+
73+ oInput :: Parser (Maybe FilePath)
74+ oInput = dashToStdin <$> do
75+ strArgument
76+ $ metavar "FILE"
77+ <> action "file"
78+ <> help "The input file containing the YAML document; use '-' for stdin."
79+ where
80+ -- dash is interpreted as stdin
81+ dashToStdin = \case
82+ "-" -> Nothing
83+ file -> Just file
14
84
15showJSON :: Show a => Either a Value -> IO b
85showJSON :: Show a => Either a Value -> IO b
16−showJSON ejson =
86+showJSON = \case
17− case ejson of
18− Left err -> hPutStrLn stderr (show err) >> exitFailure
87+ Left err -> hPrint stderr err >> exitFailure
19− Right res -> putStr (encode (res :: Value)) >> exitSuccess
88+ Right res -> putStr (encode (res :: Value)) >> exitSuccess
20
89
21main :: IO ()
90main :: IO ()
22main = do
91main = do
23− args <- getArgs
92+ Options{ optInput } <- options
24− case args of
93+ showJSON =<< case optInput of
25− -- strict getContents will read in all of stdin at once
94+ -- strict getContents will read in all of stdin at once
26− (["-h"]) -> helpMessage
95+ Nothing -> decodeEither' <$> getContents
27− (["--help"]) -> helpMessage
28− (["-"]) -> getContents >>= showJSON . decodeEither'
29− ([f]) -> decodeFileEither f >>= showJSON
96+ Just f -> decodeFileEither f
30− _ -> helpMessage
31−
yaml/package.yaml+9−8
@@ -34,7 +34,7 @@ flags:
34 default: true
34 default: true
35
35
36dependencies:
36dependencies:
37−- base >=4.9.1 && <5 # GHC 8.0.2 and later
37+- base >=4.9.1 && <5 # GHC 8.0.2 and later, meaning that Data.Semigroup is in base
38- bytestring >=0.9.1.4
38- bytestring >=0.9.1.4
39- transformers >=0.1
39- transformers >=0.1
40- mtl
40- mtl
@@ -51,15 +51,12 @@ dependencies:
51- directory
51- directory
52- template-haskell
52- template-haskell
53- libyaml >=0.1 && <0.2
53- libyaml >=0.1 && <0.2
54−when:
54+
55− condition: "!impl(ghc >= 8.0)"
55+ghc-options: -Wall -Wcompat
56− dependencies:
57− - semigroups
58
56
59library:
57library:
60 other-extensions:
58 other-extensions:
61 - LambdaCase
59 - LambdaCase
62− ghc-options: -Wall
63 source-dirs: src
60 source-dirs: src
64
61
65executables:
62executables:
@@ -75,14 +72,18 @@ executables:
75 main: yaml2json.hs
72 main: yaml2json.hs
76 source-dirs: exe
73 source-dirs: exe
77 dependencies:
74 dependencies:
75+ - optparse-applicative
78 - yaml
76 - yaml
79 when:
77 when:
80 - condition: flag(no-exe)
78 - condition: flag(no-exe)
81 buildable: false
79 buildable: false
80+ other-extensions:
81+ - CPP
82+ - LambdaCase
83+ - NamedFieldPuns
82 examples:
84 examples:
83 main: Main.hs
85 main: Main.hs
84 source-dirs: examples
86 source-dirs: examples
85− ghc-options: -Wall
86 when:
87 when:
87 - condition: flag(no-examples)
88 - condition: flag(no-examples)
88 then:
89 then:
@@ -96,7 +97,7 @@ tests:
96 spec:
97 spec:
97 main: Spec.hs
98 main: Spec.hs
98 source-dirs: test
99 source-dirs: test
99− ghc-options: -Wall "-with-rtsopts=-K1K"
100+ ghc-options: "-with-rtsopts=-K1K"
100 cpp-options: -DTEST
101 cpp-options: -DTEST
101 dependencies:
102 dependencies:
102 - hspec >=1.3
103 - hspec >=1.3
yaml/yaml.cabal+18−27
@@ -1,10 +1,8 @@
1cabal-version: 1.12
1cabal-version: 1.12
2
2
3−-- This file has been generated from package.yaml by hpack version 0.33.0.
3+-- This file has been generated from package.yaml by hpack version 0.34.4.
4--
4--
5-- see: https://github.com/sol/hpack
5-- see: https://github.com/sol/hpack
6−--
7−-- hash: 55c85c8d4d3074a558a82e30a2592ecff9db2e6f1571547c73d26ba44bfc1c20
8
6
9name: yaml
7name: yaml
10version: 0.11.5.0
8version: 0.11.5.0
@@ -62,8 +60,9 @@ library
62 Paths_yaml
60 Paths_yaml
63 hs-source-dirs:
61 hs-source-dirs:
64 src
62 src
65− other-extensions: LambdaCase
63+ other-extensions:
64+ LambdaCase
66− ghc-options: -Wall
65+ ghc-options: -Wall -Wcompat
67 build-depends:
66 build-depends:
68 aeson >=0.11
67 aeson >=0.11
69 , attoparsec >=0.11.3.0
68 , attoparsec >=0.11.3.0
@@ -73,7 +72,7 @@ library
73 , containers
72 , containers
74 , directory
73 , directory
75 , filepath
74 , filepath
76− , libyaml >=0.1 && <0.2
75+ , libyaml ==0.1.*
77 , mtl
76 , mtl
78 , resourcet >=0.3 && <1.3
77 , resourcet >=0.3 && <1.3
79 , scientific >=0.3
78 , scientific >=0.3
@@ -82,9 +81,6 @@ library
82 , transformers >=0.1
81 , transformers >=0.1
83 , unordered-containers
82 , unordered-containers
84 , vector
83 , vector
85− if !impl(ghc >= 8.0)
86− build-depends:
87− semigroups
88 default-language: Haskell2010
84 default-language: Haskell2010
89
85
90executable examples
86executable examples
@@ -95,7 +91,7 @@ executable examples
95 Paths_yaml
91 Paths_yaml
96 hs-source-dirs:
92 hs-source-dirs:
97 examples
93 examples
98− ghc-options: -Wall
94+ ghc-options: -Wall -Wcompat
99 build-depends:
95 build-depends:
100 aeson >=0.11
96 aeson >=0.11
101 , attoparsec >=0.11.3.0
97 , attoparsec >=0.11.3.0
@@ -105,7 +101,7 @@ executable examples
105 , containers
101 , containers
106 , directory
102 , directory
107 , filepath
103 , filepath
108− , libyaml >=0.1 && <0.2
104+ , libyaml ==0.1.*
109 , mtl
105 , mtl
110 , resourcet >=0.3 && <1.3
106 , resourcet >=0.3 && <1.3
111 , scientific >=0.3
107 , scientific >=0.3
@@ -114,9 +110,6 @@ executable examples
114 , transformers >=0.1
110 , transformers >=0.1
115 , unordered-containers
111 , unordered-containers
116 , vector
112 , vector
117− if !impl(ghc >= 8.0)
118− build-depends:
119− semigroups
120 if flag(no-examples)
113 if flag(no-examples)
121 buildable: False
114 buildable: False
122 else
115 else
@@ -131,6 +124,7 @@ executable json2yaml
131 Paths_yaml
124 Paths_yaml
132 hs-source-dirs:
125 hs-source-dirs:
133 exe
126 exe
127+ ghc-options: -Wall -Wcompat
134 build-depends:
128 build-depends:
135 aeson >=0.11
129 aeson >=0.11
136 , attoparsec >=0.11.3.0
130 , attoparsec >=0.11.3.0
@@ -140,7 +134,7 @@ executable json2yaml
140 , containers
134 , containers
141 , directory
135 , directory
142 , filepath
136 , filepath
143− , libyaml >=0.1 && <0.2
137+ , libyaml ==0.1.*
144 , mtl
138 , mtl
145 , resourcet >=0.3 && <1.3
139 , resourcet >=0.3 && <1.3
146 , scientific >=0.3
140 , scientific >=0.3
@@ -150,9 +144,6 @@ executable json2yaml
150 , unordered-containers
144 , unordered-containers
151 , vector
145 , vector
152 , yaml
146 , yaml
153− if !impl(ghc >= 8.0)
154− build-depends:
155− semigroups
156 if flag(no-exe)
147 if flag(no-exe)
157 buildable: False
148 buildable: False
158 default-language: Haskell2010
149 default-language: Haskell2010
@@ -163,6 +154,11 @@ executable yaml2json
163 Paths_yaml
154 Paths_yaml
164 hs-source-dirs:
155 hs-source-dirs:
165 exe
156 exe
157+ other-extensions:
158+ CPP
159+ LambdaCase
160+ NamedFieldPuns
161+ ghc-options: -Wall -Wcompat
166 build-depends:
162 build-depends:
167 aeson >=0.11
163 aeson >=0.11
168 , attoparsec >=0.11.3.0
164 , attoparsec >=0.11.3.0
@@ -172,8 +168,9 @@ executable yaml2json
172 , containers
168 , containers
173 , directory
169 , directory
174 , filepath
170 , filepath
175− , libyaml >=0.1 && <0.2
171+ , libyaml ==0.1.*
176 , mtl
172 , mtl
173+ , optparse-applicative
177 , resourcet >=0.3 && <1.3
174 , resourcet >=0.3 && <1.3
178 , scientific >=0.3
175 , scientific >=0.3
179 , template-haskell
176 , template-haskell
@@ -182,9 +179,6 @@ executable yaml2json
182 , unordered-containers
179 , unordered-containers
183 , vector
180 , vector
184 , yaml
181 , yaml
185− if !impl(ghc >= 8.0)
186− build-depends:
187− semigroups
188 if flag(no-exe)
182 if flag(no-exe)
189 buildable: False
183 buildable: False
190 default-language: Haskell2010
184 default-language: Haskell2010
@@ -199,7 +193,7 @@ test-suite spec
199 Paths_yaml
193 Paths_yaml
200 hs-source-dirs:
194 hs-source-dirs:
201 test
195 test
202− ghc-options: -Wall "-with-rtsopts=-K1K"
196+ ghc-options: -Wall -Wcompat -with-rtsopts=-K1K
203 cpp-options: -DTEST
197 cpp-options: -DTEST
204 build-depends:
198 build-depends:
205 HUnit
199 HUnit
@@ -213,7 +207,7 @@ test-suite spec
213 , directory
207 , directory
214 , filepath
208 , filepath
215 , hspec >=1.3
209 , hspec >=1.3
216− , libyaml >=0.1 && <0.2
210+ , libyaml ==0.1.*
217 , mockery
211 , mockery
218 , mtl
212 , mtl
219 , raw-strings-qq
213 , raw-strings-qq
@@ -226,7 +220,4 @@ test-suite spec
226 , unordered-containers
220 , unordered-containers
227 , vector
221 , vector
228 , yaml
222 , yaml
229− if !impl(ghc >= 8.0)
230− build-depends:
231− semigroups
232 default-language: Haskell2010
223 default-language: Haskell2010