module Faktory.Protocol
( readReply
, Reply(..)
, reply
) where
import Prelude hiding (error)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS8
import qualified Data.Text.Encoding as T
import qualified Data.Text.Read as T
import Scanner (Scanner)
import qualified Scanner
data Reply
= SingleLine ByteString
| Error ByteString
| Bulk (Maybe ByteString)
readReply :: IO ByteString -> IO (Either String (Maybe ByteString))
readReply getMore = fromScanResult <$> Scanner.scanWith getMore reply BS8.empty
where
fromScanResult = \case
Scanner.Fail _ msg -> Left msg
Scanner.More _ -> Left "Unexpected More"
Scanner.Done _ (SingleLine bs) -> Right $ Just bs
Scanner.Done _ (Error bs) -> Left $ BS8.unpack bs
Scanner.Done _ (Bulk mByteString) -> Right mByteString
{-# INLINE reply #-}
reply :: Scanner Reply
reply = do
c <- Scanner.anyChar8
case c of
'+' -> string
'-' -> error
'$' -> bulk
_ -> fail "Unknown reply type"
{-# INLINE string #-}
string :: Scanner Reply
string = SingleLine <$> line
{-# INLINE error #-}
error :: Scanner Reply
error = Error <$> line
{-# INLINE bulk #-}
bulk :: Scanner Reply
bulk = Bulk <$> do
len <- integral
if len < 0 then return Nothing else Just <$> Scanner.take len <* eol
{-# INLINE integral #-}
integral :: Integral i => Scanner i
integral = do
str <- line
case T.signed T.decimal (T.decodeUtf8 str) of
Left err -> fail (show err)
Right (l, _) -> return l
{-# INLINE line #-}
line :: Scanner ByteString
line = Scanner.takeWhileChar8 (/= '\r') <* eol
{-# INLINE eol #-}
eol :: Scanner ()
eol = do
Scanner.char8 '\r'
Scanner.char8 '\n'