{-# LANGUAGE CPP #-}

-- | Auxiliary functions for the IO monad.

module Agda.Utils.IO where

import Control.Exception
import Control.Monad.Except
import Control.Monad.State
import Control.Monad.Writer

import Agda.Utils.List (dropFrom)
import Agda.Utils.String (rtrim)

-- | Catch 'Exception's in an extension of the 'IO' monad.
--
class CatchIO m where
  catchIO :: Exception e => m a -> (e -> m a) -> m a

-- | Alias of 'catch' for the IO monad.
--
instance CatchIO IO where
  catchIO = catch

-- | Upon exception, the error is lost.

instance CatchIO m => CatchIO (ExceptT e m) where
  catchIO m h = ExceptT $ runExceptT m `catchIO` \ e -> runExceptT (h e)

-- | Upon exception, the written output is lost.
--
instance CatchIO m => CatchIO (WriterT w m) where
  catchIO m h = WriterT $ runWriterT m `catchIO` \ e -> runWriterT (h e)

-- | Upon exception, the state is reset.
--
instance CatchIO m => CatchIO (StateT s m) where
  catchIO m h = StateT $ \s -> runStateT m s `catchIO` \ e -> runStateT (h e) s

-- | Print an 'IOException' without the call stack.
--
showIOException :: Exception e => e -> String
showIOException =
  rtrim
#if MIN_VERSION_base(4,20,0)
  -- Andreas, 2024-07-05, issue #7299.
  -- Ugly hack to drop call stack (introduced in GHC 9.10) from IOException.
  -- If you have a better solution, please update this.
  . dropFrom "HasCallStack"
#endif
  . displayException
