packages feed

rfc-redis-0.0.0.1: src/RFC/Redis.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE NoImplicitPrelude #-}


module RFC.Redis
  ( createConnectionPool
  , ConnectionPool
  , HasRedis(..)
  , RedisException(..)
  , get
  , setex
  ) where

import           Data.Time.Clock ( nominalDay )
import qualified Database.Redis  as R
import           RFC.Env         as Env
import           RFC.Prelude

type ConnectionPool = R.Connection

newtype RedisException = RedisException R.Reply deriving (Typeable, Show)
instance Exception RedisException

class (MonadIO m) => HasRedis m where
  getRedisPool :: m ConnectionPool

  runRedis :: R.Redis a -> m a
  runRedis r = do
    conn <- getRedisPool
    liftIO $ R.runRedis conn r
  {-# INLINEABLE runRedis #-}

instance DefConfig R.ConnectInfo where
  defConfig = R.defaultConnectInfo
  {-# INLINE defConfig #-}

instance FromEnv R.ConnectInfo where
  fromEnv = R.ConnInfo
    <$> envWithDevDefault "REDIS_HOST" (R.connectHost defConfig)
    <*> envWithDefault "REDIS_PORT" (R.connectPort defConfig)
    <*> envWithDefault "REDIS_AUTH" (R.connectAuth defConfig)
    <*> envWithDefault "REDIS_DB" (R.connectDatabase defConfig)
    <*> envWithDefault "REDIS_MAX_CONNS" (R.connectMaxConnections defConfig)
    <*> envWithDefault "REDIS_IDLE_TIMEOUT" (R.connectMaxIdleTime defConfig)
    <*> envWithDefault "REDIS_CONN_TIMEOUT" (Just (nominalDay/24)) -- No timeout by default!
    <*> return Nothing
  {-# INLINEABLE fromEnv #-}

createConnectionPool :: (MonadUnliftIO m, MonadFail m) => m ConnectionPool
createConnectionPool = do
  connInfoResult <- liftIO decodeEnv
  case connInfoResult of
    Left err       -> fail $ "Could not configure Redis connection: " <> err
    Right connInfo -> liftIO $ R.connect connInfo
{-# INLINEABLE createConnectionPool #-}

unpack :: (MonadUnliftIO m) => Either R.Reply a -> m a
unpack (Left reply) = throwIO $ RedisException reply
unpack (Right it)   = return it
{-# INLINE unpack #-}

get :: (HasRedis m, ToText tIn, FromText tOut) => tIn -> m (Maybe tOut)
get key = do
  result <- runRedis . R.get $ toUTF8 key
  maybeResult <- liftIO $ unpack result
  return $ do
    bsResult <- maybeResult
    fromUTF8 bsResult
{-# INLINEABLE get #-}

setex :: (HasRedis m, ToText key, ToText value, TimeUnit expiry) => key -> value -> expiry -> m ()
setex key value expiry = do
    result <- runRedis $ R.setex (toUTF8 key) milliseconds (toUTF8 value)
    _ <- liftIO $ unpack result
    return ()
  where
    milliseconds = fromIntegral ( (convertUnit expiry)::Millisecond )
{-# INLINEABLE setex #-}