{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE CPP, OverloadedStrings, FlexibleInstances, MultiParamTypeClasses,
GeneralizedNewtypeDeriving #-}
module Database.Redis.Transactions (
watch, unwatch, multiExec,
Queued(), TxResult(..), RedisTx(),
) where
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
import Control.Monad.State.Strict
import Control.DeepSeq
import GHC.Generics
import Data.ByteString (ByteString)
import Data.Vector (Vector, fromList, (!))
import Database.Redis.Core
import Database.Redis.Protocol
import Database.Redis.Types
newtype RedisTx a = RedisTx (StateT Int Redis a)
deriving (Monad, MonadIO, Functor, Applicative)
runRedisTx :: RedisTx a -> Redis a
runRedisTx (RedisTx r) = evalStateT r 0
instance MonadRedis RedisTx where
liftRedis = RedisTx . lift
instance RedisCtx RedisTx Queued where
returnDecode _queued = RedisTx $ do
i <- get
put (i+1)
return $ Queued (decode . (!i))
data Queued a = Queued (Vector Reply -> Either Reply a)
instance Functor Queued where
fmap f (Queued g) = Queued (fmap f . g)
instance Applicative Queued where
pure x = Queued (const $ Right x)
Queued f <*> Queued x = Queued $ \rs -> do
f' <- f rs
x' <- x rs
return (f' x')
instance Monad Queued where
return = pure
Queued x >>= f = Queued $ \rs -> do
x' <- x rs
let Queued f' = f x'
f' rs
data TxResult a
= TxSuccess a
| TxAborted
| TxError String
deriving (Show, Eq, Generic)
instance NFData a => NFData (TxResult a)
watch
:: [ByteString]
-> Redis (Either Reply Status)
watch key = sendRequest ("WATCH" : key)
unwatch :: Redis (Either Reply Status)
unwatch = sendRequest ["UNWATCH"]
multiExec :: RedisTx (Queued a) -> Redis (TxResult a)
multiExec rtx = do
_ <- multi
Queued f <- runRedisTx rtx
r <- exec
case r of
MultiBulk rs ->
return $ maybe
TxAborted
(either (TxError . show) TxSuccess . f . fromList)
rs
_ -> error $ "hedis: EXEC returned " ++ show r
multi :: Redis (Either Reply Status)
multi = sendRequest ["MULTI"]
exec :: Redis Reply
exec = either id id <$> sendRequest ["EXEC"]