{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module App.Commands.QueryLazy ( cmdQueryLazy ) where import App.Char import Control.Applicative import Control.Lens import Control.Monad import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Resource import Data.Generics.Product.Any import Data.List import Data.Semigroup ((<>)) import Options.Applicative hiding (columns) import Text.Read (readEither) import qualified App.Commands.Options.Type as Z import qualified App.IO as IO import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as B import qualified Data.ByteString.Lazy as LBS import qualified Data.Vector as DV import qualified HaskellWorks.Data.Dsv.Lazy.Cursor as SVL import qualified HaskellWorks.Data.Dsv.Lazy.Cursor.Lazy as SVLL import qualified HaskellWorks.Data.Dsv.Lazy.Cursor.Strict as SVLS import qualified System.Exit as IO import qualified System.IO as IO defaultMethod :: String defaultMethod = "lazy-traverse" runQueryLazy :: Z.QueryLazyOptions -> IO () runQueryLazy opts = case opts ^. the @"method" of "lazy-traverse" -> runQueryLazyFast opts "strict-traverse" -> runQueryLazySlow opts "strict-bytestring" -> runQueryLazyStrict opts method -> do IO.hPutStrLn IO.stderr $ "Unknown method: " <> method IO.exitFailure runQueryLazySlow :: Z.QueryLazyOptions -> IO () runQueryLazySlow opts = do !bs <- IO.readInputFile (opts ^. the @"filePath") let !c = SVL.makeCursor (opts ^. the @"delimiter") bs let !rows = SVLL.toListVector c let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter") runResourceT $ do (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing forM_ rows $ \row -> do let fieldStrings = columnToFieldString row <$> (opts ^. the @"columns") liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10 return () return () where columnToFieldString :: DV.Vector LBS.ByteString -> Int -> B.Builder columnToFieldString fields i = if i >= 0 && i < DV.length fields then B.lazyByteString (DV.unsafeIndex fields i) else B.lazyByteString LBS.empty runQueryLazyStrict :: Z.QueryLazyOptions -> IO () runQueryLazyStrict opts = do !bs <- IO.readInputFile (opts ^. the @"filePath") let !c = SVL.makeCursor (opts ^. the @"delimiter") bs let !rows = SVLS.toListVector c let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter") runResourceT $ do (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing forM_ rows $ \row -> do let fieldStrings = columnToFieldString row <$> (opts ^. the @"columns") liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10 return () return () where columnToFieldString :: DV.Vector BS.ByteString -> Int -> B.Builder columnToFieldString fields i = if i >= 0 && i < DV.length fields then B.byteString (DV.unsafeIndex fields i) else B.byteString BS.empty runQueryLazyFast :: Z.QueryLazyOptions -> IO () runQueryLazyFast opts = do !bs <- IO.readInputFile (opts ^. the @"filePath") let !c = SVL.makeCursor (opts ^. the @"delimiter") bs let !sel = opts ^. the @"columns" let !rows = SVLL.selectListVector sel c let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter") runResourceT $ do (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing forM_ rows $ \row -> do let fieldStrings = fmap B.lazyByteString row liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10 return () return () cmdQueryLazy :: Mod CommandFields (IO ()) cmdQueryLazy = command "query-lazy" $ flip info idm $ runQueryLazy <$> optsQueryLazy nonZeroOneBased :: Mod OptionFields Int -> Parser Int nonZeroOneBased = option $ eitherReader $ \s -> do a <- readEither s if a == 0 then Left "cannot index column 0" else Right (a - 1) optsQueryLazy :: Parser Z.QueryLazyOptions optsQueryLazy = Z.QueryLazyOptions <$> many ( nonZeroOneBased ( long "column" <> short 'k' <> help "Column to select" <> metavar "COLUMN INDEX" )) <*> strOption ( long "input" <> short 'i' <> help "Input DSV file" <> metavar "FILE" <> showDefault <> value "-" ) <*> strOption ( long "output" <> short 'o' <> help "Output DSV file" <> metavar "FILE" <> showDefault <> value "-" ) <*> option readWord8 ( long "input-delimiter" <> short 'd' <> help "Input DSV delimiter" <> metavar "CHAR" ) <*> option readWord8 ( long "output-delimiter" <> short 'e' <> help "Output DSV delimiter" <> metavar "CHAR" ) <*> strOption ( long "method" <> short 'm' <> help "Method" <> metavar "METHOD" <> showDefault <> value defaultMethod )