packages feed

simplexmq-1.1.0: src/Simplex/Messaging/Server/QueueStore/STM.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}

module Simplex.Messaging.Server.QueueStore.STM where

import Control.Monad
import Data.Functor (($>))
import Simplex.Messaging.Protocol
import Simplex.Messaging.Server.QueueStore
import Simplex.Messaging.TMap (TMap)
import qualified Simplex.Messaging.TMap as TM
import Simplex.Messaging.Util (ifM)
import UnliftIO.STM

data QueueStore = QueueStore
  { queues :: TMap RecipientId (TVar QueueRec),
    senders :: TMap SenderId RecipientId,
    notifiers :: TMap NotifierId RecipientId
  }

newQueueStore :: STM QueueStore
newQueueStore = do
  queues <- TM.empty
  senders <- TM.empty
  notifiers <- TM.empty
  pure QueueStore {queues, senders, notifiers}

instance MonadQueueStore QueueStore STM where
  addQueue :: QueueStore -> QueueRec -> STM (Either ErrorType ())
  addQueue QueueStore {queues, senders} q@QueueRec {recipientId = rId, senderId = sId} = do
    ifM hasId (pure $ Left DUPLICATE_) $ do
      qVar <- newTVar q
      TM.insert rId qVar queues
      TM.insert sId rId senders
      pure $ Right ()
    where
      hasId = (||) <$> TM.member rId queues <*> TM.member sId senders

  getQueue :: QueueStore -> SParty p -> QueueId -> STM (Either ErrorType QueueRec)
  getQueue QueueStore {queues, senders, notifiers} party qId =
    toResult <$> (mapM readTVar =<< getVar)
    where
      getVar = case party of
        SRecipient -> TM.lookup qId queues
        SSender -> TM.lookup qId senders >>= get
        SNotifier -> TM.lookup qId notifiers >>= get
      get = fmap join . mapM (`TM.lookup` queues)

  secureQueue :: QueueStore -> RecipientId -> SndPublicVerifyKey -> STM (Either ErrorType QueueRec)
  secureQueue QueueStore {queues} rId sKey =
    withQueue rId queues $ \qVar ->
      readTVar qVar >>= \q -> case senderKey q of
        Just _ -> pure Nothing
        _ -> writeTVar qVar q {senderKey = Just sKey} $> Just q

  addQueueNotifier :: QueueStore -> RecipientId -> NotifierId -> NtfPublicVerifyKey -> STM (Either ErrorType QueueRec)
  addQueueNotifier QueueStore {queues, notifiers} rId nId nKey = do
    ifM (TM.member nId notifiers) (pure $ Left DUPLICATE_) $
      withQueue rId queues $ \qVar ->
        readTVar qVar >>= \q -> case notifier q of
          Just _ -> pure Nothing
          _ -> do
            writeTVar qVar q {notifier = Just (nId, nKey)}
            TM.insert nId rId notifiers
            pure $ Just q

  suspendQueue :: QueueStore -> RecipientId -> STM (Either ErrorType ())
  suspendQueue QueueStore {queues} rId =
    withQueue rId queues $ \qVar -> modifyTVar' qVar (\q -> q {status = QueueOff}) $> Just ()

  deleteQueue :: QueueStore -> RecipientId -> STM (Either ErrorType ())
  deleteQueue QueueStore {queues, senders, notifiers} rId = do
    TM.lookupDelete rId queues >>= \case
      Just qVar ->
        readTVar qVar >>= \q -> do
          TM.delete (senderId q) senders
          forM_ (notifier q) $ \(nId, _) -> TM.delete nId notifiers
          pure $ Right ()
      _ -> pure $ Left AUTH

toResult :: Maybe a -> Either ErrorType a
toResult = maybe (Left AUTH) Right

withQueue :: RecipientId -> TMap RecipientId (TVar QueueRec) -> (TVar QueueRec -> STM (Maybe a)) -> STM (Either ErrorType a)
withQueue rId queues f = toResult <$> (TM.lookup rId queues >>= fmap join . mapM f)