on master help sign in
Re #195: yaml2json --help --version options
0722db0Andreas Abel
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+5
@@ -72,10 +72,15 @@ executables:
72 main: yaml2json.hs
72 main: yaml2json.hs
73 source-dirs: exe
73 source-dirs: exe
74 dependencies:
74 dependencies:
75+ - optparse-applicative
75 - yaml
76 - yaml
76 when:
77 when:
77 - condition: flag(no-exe)
78 - condition: flag(no-exe)
78 buildable: false
79 buildable: false
80+ other-extensions:
81+ - CPP
82+ - LambdaCase
83+ - NamedFieldPuns
79 examples:
84 examples:
80 main: Main.hs
85 main: Main.hs
81 source-dirs: examples
86 source-dirs: examples
yaml/yaml.cabal+5
@@ -154,6 +154,10 @@ executable yaml2json
154 Paths_yaml
154 Paths_yaml
155 hs-source-dirs:
155 hs-source-dirs:
156 exe
156 exe
157+ other-extensions:
158+ CPP
159+ LambdaCase
160+ NamedFieldPuns
157 ghc-options: -Wall -Wcompat
161 ghc-options: -Wall -Wcompat
158 build-depends:
162 build-depends:
159 aeson >=0.11
163 aeson >=0.11
@@ -166,6 +170,7 @@ executable yaml2json
166 , filepath
170 , filepath
167 , libyaml ==0.1.*
171 , libyaml ==0.1.*
168 , mtl
172 , mtl
173+ , optparse-applicative
169 , resourcet >=0.3 && <1.3
174 , resourcet >=0.3 && <1.3
170 , scientific >=0.3
175 , scientific >=0.3
171 , template-haskell
176 , template-haskell