metro-0.1.0.1: src/Metro/Conn.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Metro.Conn
( ConnEnv
, ConnT
, FromConn (..)
, runConnT
, initConnEnv
, receive
, send
, close
, statusTVar
) where
import Control.Monad.Reader.Class (MonadReader (ask))
import Control.Monad.Trans.Class (MonadTrans, lift)
import Control.Monad.Trans.Reader (ReaderT (..), runReaderT)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B (empty)
import Metro.Class
import qualified Metro.Lock as L (Lock, new, with)
import Metro.Utils (recvEnough)
import UnliftIO
data ConnEnv tp = ConnEnv
{ transport :: tp
, readLock :: L.Lock
, writeLock :: L.Lock
, buffer :: TVar ByteString
, status :: TVar Bool
}
newtype ConnT tp m a = ConnT { unConnT :: ReaderT (ConnEnv tp) m a }
deriving
( Functor
, Applicative
, Monad
, MonadTrans
, MonadIO
, MonadReader (ConnEnv tp)
)
instance MonadUnliftIO m => MonadUnliftIO (ConnT tp m) where
withRunInIO inner = ConnT $
ReaderT $ \r ->
withRunInIO $ \run ->
inner (run . runConnT r)
class FromConn m where
fromConn :: Monad n => ConnT tp n a -> m tp n a
instance FromConn ConnT where
fromConn = id
runConnT :: ConnEnv tp -> ConnT tp m a -> m a
runConnT connEnv = flip runReaderT connEnv . unConnT
initConnEnv :: (MonadIO m, Transport tp) => TransportConfig tp -> m (ConnEnv tp)
initConnEnv config = do
readLock <- L.new
writeLock <- L.new
status <- newTVarIO True
buffer <- newTVarIO B.empty
transport <- liftIO $ newTransport config
return ConnEnv{..}
receive :: (MonadUnliftIO m, Transport tp, RecvPacket pkt) => ConnT tp m pkt
receive = do
ConnEnv{..} <- ask
L.with readLock $ lift $ recvPacket (recvEnough buffer transport)
send :: (MonadUnliftIO m, Transport tp, SendPacket pkt) => pkt -> ConnT tp m ()
send pkt = do
ConnEnv{..} <- ask
L.with writeLock $ lift $ sendPacket pkt (liftIO . sendData transport)
close :: (MonadIO m, Transport tp) => ConnT tp m ()
close = do
ConnEnv{..} <- ask
atomically $ writeTVar status False
liftIO $ closeTransport transport
statusTVar :: Monad m => ConnT tp m (TVar Bool)
statusTVar = status <$> ask