packages feed

unliftio-messagebox-2.0.0: src/UnliftIO/MessageBox/CatchAll.hs

{-# LANGUAGE Strict #-}

-- | Utilities for exception safe message boxes.
--
-- This provides a wrapper around "UnliftIO.MessageBox.Class" instances
-- to catch 'SomeException' in all methods like 'deliver' and 'receive'.
module UnliftIO.MessageBox.CatchAll
  ( CatchAllArg (..),
    CatchAllBox (..),
    CatchAllInput (..),
  )
where

import UnliftIO.MessageBox.Util.Future (Future (Future))
import UnliftIO.MessageBox.Class
  ( IsInput (..),
    IsMessageBox (..),
    IsMessageBoxArg (..),
  )
import UnliftIO (SomeException, liftIO, try)
import UnliftIO.Concurrent (threadDelay)

-- | A wrapper around values that are instances
-- of 'IsMessageBoxArg'. The factory wraps
-- the result of the delegated 'newMessageBox'
-- invocation into a 'CatchAllBox'.
newtype CatchAllArg cfg = CatchAllArg cfg
  deriving stock (Eq, Ord, Show)

-- | A wrapper around values that are instances
-- of 'IsMessageBox'.
--
-- The 'Input' type will be wrapped using
-- 'CatchAllInput'.
newtype CatchAllBox box a = CatchAllBox (box a)

-- | A wrapper around values that are instances
-- of 'IsInput'.
newtype CatchAllInput i a = CatchAllInput (i a)

instance IsMessageBoxArg cfg => IsMessageBoxArg (CatchAllArg cfg) where
  type MessageBox (CatchAllArg cfg) = CatchAllBox (MessageBox cfg)
  {-# INLINE newMessageBox #-}
  newMessageBox (CatchAllArg !cfg) = CatchAllBox <$> newMessageBox cfg
  getConfiguredMessageLimit (CatchAllArg !cfg) =
    getConfiguredMessageLimit cfg

instance IsMessageBox box => IsMessageBox (CatchAllBox box) where
  type Input (CatchAllBox box) = CatchAllInput (Input box)
  {-# INLINE newInput #-}
  newInput (CatchAllBox !b) =
    CatchAllInput <$> newInput b
  {-# INLINE receive #-}
  receive (CatchAllBox !box) =
    try @_ @SomeException
      (receive box)
      >>= \case
        Left _e -> liftIO (print _e) >> return Nothing
        Right r -> return r
  {-# INLINE receiveAfter #-}
  -- | Call the wrapped 'receiveAfter' and catch all sync exceptions.
  -- 
  -- When an exception is caught return 'Nothing'.
  receiveAfter (CatchAllBox !box) !t =
    try @_ @SomeException
      (receiveAfter box t)
      >>= \case
        Left _e -> liftIO (print _e) >> pure Nothing
        Right r -> return r
  {-# INLINE tryReceive #-}
  tryReceive (CatchAllBox !box) =
    try @_ @SomeException
      (tryReceive box)
      >>= \case
        Left _e ->
          liftIO (print _e)
            >> return
              ( Future
                  ( do
                      -- suspense...
                      threadDelay 1000
                      -- ... anyway, the truth is: there is no spoon.
                      return Nothing
                  )
              )
        Right r -> return r

instance (IsInput i) => IsInput (CatchAllInput i) where
  {-# INLINE deliver #-}
  deliver (CatchAllInput !i) !msg =
    try @_ @SomeException
      (deliver i msg)
      >>= \case
        Left _e -> liftIO (print _e) >> return False
        Right r -> return r