winery-1.3.2: app/Main.hs
{-# LANGUAGE LambdaCase, OverloadedStrings, CPP #-}
module Main where
import Control.Monad
import qualified Data.Aeson as JSON
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Prettyprinter
import Prettyprinter.Render.Terminal
import Data.Word (Word64)
import qualified Codec.Winery.Query as Q
import Codec.Winery.Query.Parser
import Codec.Winery
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.IO
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Text.Megaparsec
data Options = Options
{ streamInput :: Bool
, separateSchema :: Maybe (Maybe FilePath)
, printSchema :: Bool
, outputJSON :: Bool
}
defaultOptions :: Options
defaultOptions = Options
{ streamInput = False
, printSchema = False
, separateSchema = Nothing
, outputJSON = False
}
options :: [OptDescr (Options -> Options)]
options =
[ Option "s" ["stream"] (NoArg $ \o -> o { streamInput = True }) "streaming input: each payload is prefixed by little-endian Word64"
, Option "S" ["separate-schema"] (OptArg (\s o -> o { separateSchema = Just s }) "PATH") "the schema is stored separately on PATH"
, Option "" ["print-schema"] (NoArg $ \o -> o { printSchema = True }) "print the schema"
, Option "J" ["JSON"] (NoArg $ \o -> o { outputJSON = True }) "print as JSON"
]
getRight :: Either WineryException a -> IO a
getRight (Left err) = do
hPutDoc stderr $ prettyWineryException err <> hardline
exitFailure
getRight (Right a) = return a
putTerm :: Doc AnsiStyle -> IO ()
putTerm t = putDoc $ t <> hardline
getLengthPrefixed :: Handle -> IO B.ByteString
getLengthPrefixed h = do
str <- B.hGet h 8
B.hGet h (fromIntegral (evalDecoder decodeCurrent str :: Word64))
app :: Options -> Q.Query (Doc AnsiStyle) (Doc AnsiStyle) -> Handle -> IO ()
app o q h = do
let p
| outputJSON o = pretty . T.decodeUtf8 . BL.toStrict . JSON.encode
| otherwise = pretty
let getDec = getRight . getDecoderBy (Q.runQuery q $ Extractor $ const $ pure (pure . p))
printer <- case separateSchema o of
Just mpath -> do
bs <- maybe (getLengthPrefixed h) B.readFile mpath
sch <- getRight $ deserialiseSchema bs
when (printSchema o) $ putDoc $ pretty sch <> hardline
dec <- getDec sch
return $ mapM_ putTerm . evalDecoder dec
Nothing -> return $ \bs_ -> do
(s, bs) <- getRight $ splitSchema bs_
dec <- getDec s
when (printSchema o) $ putDoc $ pretty s <> hardline
mapM_ putTerm $ evalDecoder dec bs
case streamInput o of
False -> B.hGetContents h >>= printer
True -> forever $ getLengthPrefixed h >>= printer
main :: IO ()
main = getOpt Permute options <$> getArgs >>= \case
(fs, qs : paths, []) -> do
let o = foldl (flip id) defaultOptions fs
q <- case parse (parseQuery <* eof) "argument" $ T.pack qs of
Left e -> do
hPutStrLn stderr $ errorBundlePretty e
exitWith (ExitFailure 2)
Right a -> pure a
forM_ paths $ \case
"-" -> app o q stdin
path -> withFile path ReadMode (app o q)
(_, _, es) -> do
name <- getProgName
die $ unlines es ++ usageInfo name options