packages feed

supernova-0.0.1: src/Pulsar/Producer.hs

{- Defines a high-level Pulsar producer for the end user -}
module Pulsar.Producer where

import qualified Control.Monad.Catch           as E
import           Control.Monad.Managed
import           Data.IORef
import           Data.Text                      ( Text )
import qualified Pulsar.Core                   as C
import           Pulsar.Connection
import           Pulsar.Types
import           UnliftIO.Chan

{- | An abstract 'Producer' able to 'produce' messages of type 'PulsarMessage'. -}
newtype Producer m = Producer
  { produce :: PulsarMessage -> m () -- ^ Produces a single message.
  }

data ProducerState = ProducerState
  { stSeqId :: SeqId -- an incremental message sequence counter
  , stName :: Text   -- a unique name
  }

mkSeqId :: MonadIO m => IORef ProducerState -> m SeqId
mkSeqId ref = liftIO $ atomicModifyIORef
  ref
  (\(ProducerState s n) -> let s' = s + 1 in (ProducerState s' n, s))

{- | Create a new 'Producer' by supplying a 'PulsarCtx' (returned by 'Pulsar.connect') and a 'Topic'. -}
newProducer
  :: (MonadManaged m, MonadIO f) => PulsarCtx -> Topic -> m (Producer f)
newProducer (Ctx conn app) topic = do
  chan  <- newChan
  pid   <- mkProducerId chan app
  pname <- liftIO $ mkProducer chan pid
  pst   <- liftIO $ newIORef (ProducerState 0 pname)
  using $ managed
    (E.bracket (pure $ Producer (dispatch chan pid pst))
               (const $ newReq >>= \r -> C.closeProducer conn chan r pid)
    )
 where
  newReq = mkRequestId app
  dispatch chan pid pst msg = do
    sid <- mkSeqId pst
    liftIO $ C.send conn chan pid sid msg
  mkProducer chan pid = do
    req1 <- newReq
    C.lookup conn chan req1 topic
    req2 <- newReq
    C.newProducer conn chan req2 pid topic