-- Copyright 2021 Evan Laforge
-- This program is distributed under the terms of the GNU General Public
-- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt

{-# LANGUAGE ScopedTypeVariables #-}
-- | Utilities for exceptions.
module Util.Exceptions where
import qualified Control.Exception as Exception
import           Control.Monad (guard, void)
import qualified System.IO.Error as IO.Error


-- | If @op@ raised ENOENT, return Nothing.
ignoreEnoent :: IO a -> IO (Maybe a)
ignoreEnoent :: forall a. IO a -> IO (Maybe a)
ignoreEnoent = (IOError -> Bool) -> IO a -> IO (Maybe a)
forall e a. Exception e => (e -> Bool) -> IO a -> IO (Maybe a)
ignoreError IOError -> Bool
IO.Error.isDoesNotExistError

ignoreEnoent_ :: IO a -> IO ()
ignoreEnoent_ :: forall a. IO a -> IO ()
ignoreEnoent_ = IO (Maybe a) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Maybe a) -> IO ()) -> (IO a -> IO (Maybe a)) -> IO a -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO a -> IO (Maybe a)
forall a. IO a -> IO (Maybe a)
ignoreEnoent

ignoreEOF :: IO a -> IO (Maybe a)
ignoreEOF :: forall a. IO a -> IO (Maybe a)
ignoreEOF = (IOError -> Bool) -> IO a -> IO (Maybe a)
forall e a. Exception e => (e -> Bool) -> IO a -> IO (Maybe a)
ignoreError IOError -> Bool
IO.Error.isEOFError

-- | Ignore all IO errors.  This is useful when you want to see if a file
-- exists, because some-file/x will not give ENOENT, but ENOTDIR, which is
-- probably isIllegalOperation.
ignoreIOError :: IO a -> IO (Maybe a)
ignoreIOError :: forall a. IO a -> IO (Maybe a)
ignoreIOError = (IOError -> Bool) -> IO a -> IO (Maybe a)
forall e a. Exception e => (e -> Bool) -> IO a -> IO (Maybe a)
ignoreError (\(IOError
_ :: IO.Error.IOError) -> Bool
True)

ignoreError :: Exception.Exception e => (e -> Bool) -> IO a -> IO (Maybe a)
ignoreError :: forall e a. Exception e => (e -> Bool) -> IO a -> IO (Maybe a)
ignoreError e -> Bool
ignore IO a
action = (e -> Maybe ())
-> (() -> IO (Maybe a)) -> IO (Maybe a) -> IO (Maybe a)
forall e b a.
Exception e =>
(e -> Maybe b) -> (b -> IO a) -> IO a -> IO a
Exception.handleJust (Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (e -> Bool) -> e -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Bool
ignore)
    (IO (Maybe a) -> () -> IO (Maybe a)
forall a b. a -> b -> a
const (Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing)) ((a -> Maybe a) -> IO a -> IO (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just IO a
action)

-- | 'Exception.try' specialized to IOError.
tryIO :: IO a -> IO (Either IO.Error.IOError a)
tryIO :: forall a. IO a -> IO (Either IOError a)
tryIO = IO a -> IO (Either IOError a)
forall e a. Exception e => IO a -> IO (Either e a)
Exception.try