{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Utils
( MockBoxInit (..),
MockBox (..),
NoOpArg (..),
NoOpBox (..),
NoOpInput (..),
runTestApp,
)
where
import RIO
import UnliftIO.MessageBox.Class
( IsInput (..),
IsMessageBox (Input, newInput, receive, tryReceive),
IsMessageBoxArg (..),
)
import UnliftIO.MessageBox.Util.Future (Future (Future))
runTestApp :: MonadUnliftIO m => RIO LogFunc b -> m b
runTestApp x = do
let isVerbose = True
logToStdErr <- logOptionsHandle stdout isVerbose
withLogFunc logToStdErr $ flip runRIO x
-- message box implementation
-- NOTE: Because of parametricity and the existential quantification
-- of the message payload, the receive and deliver methods
-- are only capable of throwing exceptions or bottoming out
data MockBoxInit msgBox = MkMockBoxInit
{ mockBoxNew :: forall m a. MonadUnliftIO m => m (msgBox a),
mockBoxLimit :: !(Maybe Int)
}
instance IsMessageBox msgBox => IsMessageBoxArg (MockBoxInit msgBox) where
type MessageBox (MockBoxInit msgBox) = msgBox
getConfiguredMessageLimit = mockBoxLimit
newMessageBox b = mockBoxNew b
data MockBox input a = MkMockBox
{ mockBoxNewInput :: forall m. MonadUnliftIO m => m (input a),
mockBoxReceive :: forall m. MonadUnliftIO m => m (Maybe a),
mockBoxTryReceive :: forall m. MonadUnliftIO m => m (Future a)
}
instance IsInput input => IsMessageBox (MockBox input) where
type Input (MockBox input) = input
newInput m = mockBoxNewInput m
receive m = mockBoxReceive m
tryReceive m = mockBoxTryReceive m
data NoOpArg
= NoOpArg
deriving stock (Show)
newtype NoOpInput a
= OnDeliver (forall m. MonadUnliftIO m => a -> m Bool)
data NoOpBox a
= OnReceive (Maybe Int) (Maybe a)
deriving stock (Show)
instance IsMessageBoxArg NoOpArg where
type MessageBox NoOpArg = NoOpBox
newMessageBox NoOpArg = return (OnReceive Nothing Nothing)
getConfiguredMessageLimit _ = Nothing
instance IsMessageBox NoOpBox where
type Input NoOpBox = NoOpInput
newInput _ = return $ OnDeliver (const (return False))
receive (OnReceive t r) = do
traverse_ threadDelay t
return r
tryReceive (OnReceive t r) = do
timeoutVar <- traverse registerDelay t
return
( Future
( do
isOver <- fromMaybe True <$> traverse readTVarIO timeoutVar
if isOver
then return r
else return Nothing
)
)
instance IsInput NoOpInput where
deliver (OnDeliver react) m =
react m