-- |Combinators that catch exceptions in 'IO' and embed them into 'Sem'.
module Incipit.Exception where

import qualified Control.Exception as Base
import Control.Exception (Exception, SomeException)
import Data.Bifunctor (first)
import Data.Either (Either)
import Data.Function ((.))
import Data.Functor (fmap, void)
import Data.Maybe (Maybe)
import Polysemy (Embed, Member, Sem, embed)
import System.IO (IO)
import System.IO.Error (IOError)

import Incipit.Either (rightToMaybe)
import Incipit.String.Conversion (show)
import Incipit.String.Reexport (Text)

-- |Run an 'IO' via 'Embed' and catch exceptions of type @e@, returning 'Either'.
-- Unlike all other combinators, this doesn't convert the exception to 'Text'.
tryIOE ::
   e r a .
  Exception e =>
  Member (Embed IO) r =>
  IO a ->
  Sem r (Either e a)
tryIOE :: forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either e a)
tryIOE =
  forall (m :: * -> *) (r :: [(* -> *) -> * -> *]) a.
Member (Embed m) r =>
m a -> Sem r a
embed @IO (IO (Either e a) -> Sem r (Either e a))
-> (IO a -> IO (Either e a)) -> IO a -> Sem r (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e a. Exception e => IO a -> IO (Either e a)
Base.try @e
{-# inline tryIOE #-}

-- |Run an 'IO' via 'Embed' and catch exceptions of type @e@, returning 'Either'.
tryIO ::
   e r a .
  Exception e =>
  Member (Embed IO) r =>
  IO a ->
  Sem r (Either Text a)
tryIO :: forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO =
  forall (m :: * -> *) (r :: [(* -> *) -> * -> *]) a.
Member (Embed m) r =>
m a -> Sem r a
embed @IO (IO (Either Text a) -> Sem r (Either Text a))
-> (IO a -> IO (Either Text a)) -> IO a -> Sem r (Either Text a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Either e a -> Either Text a)
-> IO (Either e a) -> IO (Either Text a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((e -> Text) -> Either e a -> Either Text a
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first e -> Text
forall b a. (Show a, IsString b) => a -> b
show) (IO (Either e a) -> IO (Either Text a))
-> (IO a -> IO (Either e a)) -> IO a -> IO (Either Text a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e a. Exception e => IO a -> IO (Either e a)
Base.try @e
{-# inline tryIO #-}

-- |Run an 'IO' via 'Embed' and catch IOError, returning 'Either'.
tryIOError ::
   r a .
  Member (Embed IO) r =>
  IO a ->
  Sem r (Either Text a)
tryIOError :: forall (r :: [(* -> *) -> * -> *]) a.
Member (Embed IO) r =>
IO a -> Sem r (Either Text a)
tryIOError =
  forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO @IOError
{-# inline tryIOError #-}

-- |Run an 'IO' via 'Embed' and catch all exceptions, returning 'Either'.
tryAny ::
   r a .
  Member (Embed IO) r =>
  IO a ->
  Sem r (Either Text a)
tryAny :: forall (r :: [(* -> *) -> * -> *]) a.
Member (Embed IO) r =>
IO a -> Sem r (Either Text a)
tryAny =
  forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO @SomeException
{-# inline tryAny #-}

-- |Run an 'IO' via 'Embed' and catch exceptions of type @e@, returning 'Maybe'.
tryIOMaybe ::
   e r a .
  Exception e =>
  Member (Embed IO) r =>
  IO a ->
  Sem r (Maybe a)
tryIOMaybe :: forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Maybe a)
tryIOMaybe =
  (Either Text a -> Maybe a)
-> Sem r (Either Text a) -> Sem r (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Either Text a -> Maybe a
forall l r. Either l r -> Maybe r
rightToMaybe (Sem r (Either Text a) -> Sem r (Maybe a))
-> (IO a -> Sem r (Either Text a)) -> IO a -> Sem r (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO @e
{-# inline tryIOMaybe #-}

-- |Run an 'IO' via 'Embed' and catch 'IOError', returning 'Maybe'.
tryIOErrorMaybe ::
   r a .
  Member (Embed IO) r =>
  IO a ->
  Sem r (Maybe a)
tryIOErrorMaybe :: forall (r :: [(* -> *) -> * -> *]) a.
Member (Embed IO) r =>
IO a -> Sem r (Maybe a)
tryIOErrorMaybe =
  forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Maybe a)
tryIOMaybe @IOError
{-# inline tryIOErrorMaybe #-}

-- |Run an 'IO' via 'Embed' and catch all exceptions, returning 'Maybe'.
tryMaybe ::
   r a .
  Member (Embed IO) r =>
  IO a ->
  Sem r (Maybe a)
tryMaybe :: forall (r :: [(* -> *) -> * -> *]) a.
Member (Embed IO) r =>
IO a -> Sem r (Maybe a)
tryMaybe =
  forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Maybe a)
tryIOMaybe @SomeException
{-# inline tryMaybe #-}

-- |Run an 'IO' via 'Embed' and catch and ignore exceptions of type @e@.
tryIO_ ::
   e r .
  Exception e =>
  Member (Embed IO) r =>
  IO () ->
  Sem r ()
tryIO_ :: forall e (r :: [(* -> *) -> * -> *]).
(Exception e, Member (Embed IO) r) =>
IO () -> Sem r ()
tryIO_ =
  Sem r (Either Text ()) -> Sem r ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Sem r (Either Text ()) -> Sem r ())
-> (IO () -> Sem r (Either Text ())) -> IO () -> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO @e
{-# inline tryIO_ #-}

-- |Run an 'IO' via 'Embed' and catch and ignore 'IOError'.
tryIOError_ ::
   r .
  Member (Embed IO) r =>
  IO () ->
  Sem r ()
tryIOError_ :: forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
IO () -> Sem r ()
tryIOError_ =
  Sem r (Either Text ()) -> Sem r ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Sem r (Either Text ()) -> Sem r ())
-> (IO () -> Sem r (Either Text ())) -> IO () -> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO @IOError
{-# inline tryIOError_ #-}

-- |Run an 'IO' via 'Embed' and catch and ignore all exceptions.
tryAny_ ::
   r .
  Member (Embed IO) r =>
  IO () ->
  Sem r ()
tryAny_ :: forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
IO () -> Sem r ()
tryAny_ =
  Sem r (Either Text ()) -> Sem r ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Sem r (Either Text ()) -> Sem r ())
-> (IO () -> Sem r (Either Text ())) -> IO () -> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e (r :: [(* -> *) -> * -> *]) a.
(Exception e, Member (Embed IO) r) =>
IO a -> Sem r (Either Text a)
tryIO @SomeException
{-# inline tryAny_ #-}

-- |Run an 'IO' via 'Embed' and catch and ignore all exceptions.
ignoreException ::
  Member (Embed IO) r =>
  IO () ->
  Sem r ()
ignoreException :: forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
IO () -> Sem r ()
ignoreException =
  IO () -> Sem r ()
forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
IO () -> Sem r ()
tryAny_
{-# inline ignoreException #-}
{-# deprecated ignoreException "renamed to 'tryAny_'" #-}