{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} import Data.Aeson (encode, Value) import qualified Data.ByteString as S (getContents) import qualified Data.ByteString.Lazy as L (putStr, writeFile) #if !MIN_VERSION_base(4,11,0) import Data.Semigroup (Semigroup(..)) #endif import Data.Yaml (decodeFileEither, decodeEither') import System.Exit (die) import Options.Applicative ( Parser , action, execParser, headerDoc, help, helper, info , long, metavar, short, strArgument, strOption, value ) import Options.Applicative.Help.Pretty ( vcat, text ) import Common ( versionText, versionOption, numericVersionOption, dashToNothing ) data Options = Options { optInput :: Maybe FilePath -- ^ 'Nothing' means @stdin@. , optOutput :: Maybe FilePath -- ^ 'Nothing' means @stdout@. } -- | Name of the executable. self :: String self = "yaml2json" -- | Parse options; handle parse errors, @--help@, @--version@. options :: IO Options options = execParser $ info (helper <*> versionOption self <*> numericVersionOption <*> programOptions) (headerDoc hdoc) where hdoc = Just $ vcat $ map text [ versionText self , "Convert YAML to JSON." ] programOptions = Options <$> oInput <*> oOutput oInput :: Parser (Maybe FilePath) oInput = dashToNothing <$> do strArgument $ metavar "IN" <> value "-" <> action "file" <> help "The input file containing the YAML document; use '-' for stdin (default)." oOutput :: Parser (Maybe FilePath) oOutput = dashToNothing <$> do strOption $ long "output" <> short 'o' <> value "-" <> metavar "OUT" <> action "file" <> help "The file to hold the produced JSON document; use '-' for stdout (default)." main :: IO () main = do Options{ optInput, optOutput } <- options -- Input YAML. result <- case optInput of -- strict getContents will read in all of stdin at once Nothing -> decodeEither' <$> S.getContents Just f -> decodeFileEither f -- Output JSON. case result of Left err -> die $ show err Right val -> do let json = encode (val :: Value) maybe L.putStr L.writeFile optOutput json