-- | Modified version of @"Database.Redis.Protocol"@ -- -- -- -- Faktory takes a lot of inspiration from Redis, so the connection and -- protocol-related code translated well with minor simplifications. -- 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'