packages feed

franz-0.2.1: app/client.hs

{-# LANGUAGE LambdaCase #-}
module Main where
import Database.Franz
import Database.Franz.Network

import Control.Monad
import Control.Concurrent.STM
import Data.Function (fix)
import Data.Functor.Identity
import Data.List (foldl')
import qualified Data.ByteString.Char8 as B
import Network.Socket (PortNumber)
import System.Environment
import System.IO
import System.Console.GetOpt
import System.Exit

parseHostPort :: String -> (String -> PortNumber -> r) -> r
parseHostPort str k = case break (==':') str of
  (host, ':' : port) -> k host (read port)
  (host, _) -> k host defaultPort

data Options = Options
  { host :: String
  , timeout :: Double
  , index :: Maybe B.ByteString
  , ranges :: [(Int, Int)]
  , beginning :: Maybe Int
  , prefixLength :: Bool
  }

readOffset :: String -> Int
readOffset ('_' : n) = -read n
readOffset n = read n

options :: [OptDescr (Options -> Options)]
options = [Option "h" ["host"] (ReqArg (\str o -> o { host = str }) "HOST:PORT") "stream input"
  , Option "r" ["range"] (ReqArg (\str o -> o { ranges = case break (==':') str of
      (begin, ':' : end) -> (readOffset begin, readOffset end) : ranges o
      _ -> (readOffset str, readOffset str) : ranges o
      }) "FROM:TO") "ranges"
  , Option "b" ["begin"] (ReqArg (\str o -> o { beginning = Just $! readOffset str }) "pos") "get all the contents from this position"
  , Option "t" ["timeout"] (ReqArg (\str o -> o { timeout = read str }) "SECONDS") "Timeout"
  , Option "i" ["index"] (ReqArg (\str o -> o { index = Just $ B.pack str }) "NAME") "Index name"
  , Option "" ["prefix-length"] (NoArg (\o -> o { prefixLength = True })) "Prefix payloads by their lengths"
  ]

defaultOptions :: Options
defaultOptions = Options
  { host = "localhost"
  , timeout = 1e12
  , ranges = []
  , beginning = Nothing
  , index = Nothing
  , prefixLength = False
  }

printBS :: Options -> (a, b, B.ByteString) -> IO ()
printBS o (_, _, bs) = do
  when (prefixLength o) $ print $ B.length bs
  B.hPutStr stdout bs
  hFlush stdout

main :: IO ()
main = getOpt Permute options <$> getArgs >>= \case
  (fs, [name], []) -> do
    let o = foldl' (flip id) defaultOptions fs
    parseHostPort (host o) withConnection mempty $ \conn -> do
      let name' = B.pack name
      let timeout' = floor $ timeout o * 1000000
      let f = maybe BySeqNum ByIndex (index o)
      let req i j = Query name' (f i) (f j) AllItems
      forM_ (reverse $ ranges o) $ \(i, j) -> fetchSimple conn timeout' (req i j)
        >>= mapM_ (printBS o)
      forM_ (beginning o) $ \start -> flip fix start $ \self i -> do
        bss <- fetchSimple conn timeout' (req i i)
        mapM_ (printBS o) bss
        unless (null bss) $ self $ let (j, _, _) = last bss in j + 1

  (_, _, es) -> do
    name <- getProgName
    die $ unlines ("franz [OPTIONS] STREAM" : es) ++ usageInfo name options