on master help sign in
RE #195: json2yaml now uses optparse-applicative

The help text now looks like this: Reads a JSON document and writes it out as YAML document. Usage: json2yaml [IN] [OUT] Available options: -h,--help Show this help text -V,--version Show version info. --numeric-version Show just version number. IN The input file containing the JSON document; use '-' for stdin. OUT The file to hold the produced YAML document; use '-' for stdout. Only semantic change: use `die` instead of `error`, so the error json2yaml: Invalid input JSON CallStack (from HasCallStack): error, called at exe/json2yaml.hs:19:20 in main:Main is now just json2yaml: Invalid input JSON

commit90e3cd20fee97ef72fe9b8bd7a7ef0e7585538f5 authorAndreas Abel <andreas.abel@ifi.lmu.de> dateWed 15 Sep 2021, 09:35 (5 years ago) parent620ef8e
3 files changed+76−17
yaml/exe/json2yaml.hs+73−17 yaml/package.yaml+1 yaml/yaml.cabal+2
yaml/exe/json2yaml.hs+73−17
@@ -1,22 +1,78 @@
1−import qualified Data.Aeson as J
2−import qualified Data.ByteString as S
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NamedFieldPuns #-}
3+
4+import qualified Data.Aeson as J
5+import qualified Data.ByteString as S
36import qualified Data.ByteString.Lazy as L
4−import System.Environment (getArgs)
7+import qualified Data.Yaml as Y
8+#if !MIN_VERSION_base(4,11,0)
9+import Data.Semigroup ( Semigroup(..) )
10+#endif
11+
12+import Options.Applicative
13+ ( Parser
14+ , action, execParser, header, help, helper, info
15+ , metavar, strArgument, value
16+ )
17+
18+import System.Exit ( die )
19+
20+import Common ( dashToNothing, versionOption, numericVersionOption )
21+
22+data Options = Options
23+ { optInput :: Maybe FilePath
24+ -- ^ 'Nothing' means @stdin@.
25+ , optOutput :: Maybe FilePath
26+ -- ^ 'Nothing' means @stdout@.
27+ }
28+
29+-- | Name of the executable.
30+
31+self :: String
32+self = "json2yaml"
33+
34+-- | Parse options; handle parse errors, @--help@, @--version@.
535
6−import qualified Data.Yaml as Y
36+options :: IO Options
37+options =
38+ execParser $
39+ info (helper <*> versionOption self <*> numericVersionOption <*> programOptions)
40+ (header "Reads a JSON document and writes it out as YAML document.")
41+
42+ where
43+ programOptions = Options <$> oInput <*> oOutput
44+
45+ oInput :: Parser (Maybe FilePath)
46+ oInput = dashToNothing <$> do
47+ strArgument
48+ $ metavar "IN"
49+ <> value "-"
50+ <> action "file"
51+ <> help "The input file containing the JSON document; use '-' for stdin."
52+
53+ oOutput :: Parser (Maybe FilePath)
54+ oOutput = dashToNothing <$> do
55+ strArgument
56+ $ metavar "OUT"
57+ <> value "-"
58+ <> action "file"
59+ <> help "The file to hold the produced YAML document; use '-' for stdout."
60+
61+-- | Exit with 'self'-stamped error message.
62+
63+abort :: String -> IO a
64+abort msg = die $ concat [ self, ": ", msg ]
765
866main :: IO ()
967main = do
10− args <- getArgs
11− (input, output) <- case args ++ replicate (2 - length args) "-" of
12− [i, o] -> return (i, o)
13− _ -> fail "Usage: json2yaml [in] [out]"
14− mval <- fmap J.decode $
15− case input of
16− "-" -> L.getContents
17− _ -> L.readFile input
18− case mval of
19− Nothing -> error "Invalid input JSON"
20− Just val -> case output of
21− "-" -> S.putStr $ Y.encode (val :: Y.Value)
22− _ -> Y.encodeFile output val
68+ Options{ optInput, optOutput } <- options
69+
70+ -- Read JSON.
71+ mval <- J.decode <$> maybe L.getContents L.readFile optInput
72+
73+ -- Write YAML.
74+ case mval of
75+ Nothing -> abort "Invalid input JSON"
76+ Just val -> case optOutput of
77+ Nothing -> S.putStr $ Y.encode (val :: Y.Value)
78+ Just f -> Y.encodeFile f val
yaml/package.yaml+1
@@ -64,6 +64,7 @@ executables:
6464 main: json2yaml.hs
6565 source-dirs: exe
6666 dependencies:
67+ - optparse-applicative
6768 - yaml
6869 when:
6970 - condition: flag(no-exe)
yaml/yaml.cabal+2
@@ -121,6 +121,7 @@ executable examples
121121executable json2yaml
122122 main-is: json2yaml.hs
123123 other-modules:
124+ Common
124125 Paths_yaml
125126 hs-source-dirs:
126127 exe
@@ -136,6 +137,7 @@ executable json2yaml
136137 , filepath
137138 , libyaml ==0.1.*
138139 , mtl
140+ , optparse-applicative
139141 , resourcet >=0.3 && <1.3
140142 , scientific >=0.3
141143 , template-haskell