{-# LANGUAGE UndecidableInstances #-}

module Effectful.Notify.Internal.Data.NotifyException
  ( NotifyException (..),
  )
where

import Control.Exception
  ( Exception (displayException),
    SomeException,
  )
import Data.Text qualified as T
import Data.Text.Display (display)
import Effectful.Notify.Internal.Data.Note (Note)
import Effectful.Notify.Internal.Data.NotifySystem (NotifySystem)
import Optics.Core (A_Lens, LabelOptic (labelOptic), (^.))
import Optics.Core qualified as O

-- | Exceptions thrown when sending notifications. The list of non-fatal
-- exceptions includes:
--
-- - DBus: Non-fatal client errors.
--
-- All others are considered fatal.
--
-- @since 0.1
data NotifyException = MkNotifyException
  { -- | Underlying exception.
    NotifyException -> SomeException
exception :: SomeException,
    -- | Is fatal?
    NotifyException -> Bool
fatal :: Bool,
    -- | Note we attempted to send.
    NotifyException -> Note
note :: Note,
    -- | Notification system.
    NotifyException -> NotifySystem
notifySystem :: NotifySystem
  }

-- | @since 0.1
deriving stock instance Show NotifyException

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ SomeException, b ~ SomeException) =>
  LabelOptic "exception" k NotifyException NotifyException a b
  where
  labelOptic :: Optic k NoIx NotifyException NotifyException a b
labelOptic = LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall s t a b. LensVL s t a b -> Lens s t a b
O.lensVL (LensVL NotifyException NotifyException a b
 -> Lens NotifyException NotifyException a b)
-> LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkNotifyException SomeException
a1 Bool
a2 Note
a3 NotifySystem
a4) ->
    (SomeException -> NotifyException)
-> f SomeException -> f NotifyException
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\SomeException
b -> SomeException -> Bool -> Note -> NotifySystem -> NotifyException
MkNotifyException SomeException
b Bool
a2 Note
a3 NotifySystem
a4) (a -> f b
f a
SomeException
a1)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ Bool, b ~ Bool) =>
  LabelOptic "fatal" k NotifyException NotifyException a b
  where
  labelOptic :: Optic k NoIx NotifyException NotifyException a b
labelOptic = LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall s t a b. LensVL s t a b -> Lens s t a b
O.lensVL (LensVL NotifyException NotifyException a b
 -> Lens NotifyException NotifyException a b)
-> LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkNotifyException SomeException
a1 Bool
a2 Note
a3 NotifySystem
a4) ->
    (Bool -> NotifyException) -> f Bool -> f NotifyException
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Bool
b -> SomeException -> Bool -> Note -> NotifySystem -> NotifyException
MkNotifyException SomeException
a1 Bool
b Note
a3 NotifySystem
a4) (a -> f b
f a
Bool
a2)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ Note, b ~ Note) =>
  LabelOptic "note" k NotifyException NotifyException a b
  where
  labelOptic :: Optic k NoIx NotifyException NotifyException a b
labelOptic = LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall s t a b. LensVL s t a b -> Lens s t a b
O.lensVL (LensVL NotifyException NotifyException a b
 -> Lens NotifyException NotifyException a b)
-> LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkNotifyException SomeException
a1 Bool
a2 Note
a3 NotifySystem
a4) ->
    (Note -> NotifyException) -> f Note -> f NotifyException
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Note
b -> SomeException -> Bool -> Note -> NotifySystem -> NotifyException
MkNotifyException SomeException
a1 Bool
a2 Note
b NotifySystem
a4) (a -> f b
f a
Note
a3)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ NotifySystem, b ~ NotifySystem) =>
  LabelOptic "notifySystem" k NotifyException NotifyException a b
  where
  labelOptic :: Optic k NoIx NotifyException NotifyException a b
labelOptic = LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall s t a b. LensVL s t a b -> Lens s t a b
O.lensVL (LensVL NotifyException NotifyException a b
 -> Lens NotifyException NotifyException a b)
-> LensVL NotifyException NotifyException a b
-> Lens NotifyException NotifyException a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkNotifyException SomeException
a1 Bool
a2 Note
a3 NotifySystem
a4) ->
    (NotifySystem -> NotifyException)
-> f NotifySystem -> f NotifyException
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\NotifySystem
b -> SomeException -> Bool -> Note -> NotifySystem -> NotifyException
MkNotifyException SomeException
a1 Bool
a2 Note
a3 NotifySystem
b) (a -> f b
f a
NotifySystem
a4)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance Exception NotifyException where
  displayException :: NotifyException -> String
displayException NotifyException
ex =
    Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$
      [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
        [ Text
fatalStr,
          Text
"exception sending notification with system '",
          NotifySystem -> Text
forall a. Display a => a -> Text
display (NotifyException
ex NotifyException
-> Optic' A_Lens NoIx NotifyException NotifySystem -> NotifySystem
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx NotifyException NotifySystem
#notifySystem),
          Text
"' and note '",
          String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Note -> String
forall a. Show a => a -> String
show (NotifyException
ex NotifyException -> Optic' A_Lens NoIx NotifyException Note -> Note
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx NotifyException Note
#note),
          Text
"': ",
          String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ SomeException -> String
forall e. Exception e => e -> String
displayException (NotifyException
ex NotifyException
-> Optic' A_Lens NoIx NotifyException SomeException
-> SomeException
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx NotifyException SomeException
#exception)
        ]
    where
      fatalStr :: Text
fatalStr = case NotifyException
ex NotifyException -> Optic' A_Lens NoIx NotifyException Bool -> Bool
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx NotifyException Bool
#fatal of
        Bool
False -> Text
"Non-fatal "
        Bool
True -> Text
"Fatal "