{-# LANGUAGE GeneralizedNewtypeDeriving #-}

{- |
Copyright : Flipstone Technology Partners 2023
License   : MIT
Stability : Stable

@since 1.0.0.0
-}
module Orville.PostgreSQL.Monad.Orville
  ( Orville
  , runOrville
  , runOrvilleWithState
  )
where

import qualified Control.Exception.Safe as ExSafe
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Reader (ReaderT, runReaderT)

import qualified Orville.PostgreSQL.ErrorDetailLevel as ErrorDetailLevel
import qualified Orville.PostgreSQL.Monad.HasOrvilleState as HasOrvilleState
import qualified Orville.PostgreSQL.Monad.MonadOrville as MonadOrville
import qualified Orville.PostgreSQL.OrvilleState as OrvilleState
import Orville.PostgreSQL.Raw.Connection (ConnectionPool)

{- |
  The 'Orville' Monad provides an easy starter implementation of
  'MonadOrville.MonadOrville' when you don't have a monad specific to your
  application that you need to use.

  If you want to add Orville capabilities to your own monad, take a look at
  'MonadOrville.MonadOrville' to learn what needs to be done.

@since 1.0.0.0
-}
newtype Orville a = Orville
  { forall a. Orville a -> ReaderT OrvilleState IO a
unwrapOrville :: ReaderT OrvilleState.OrvilleState IO a
  }
  deriving
    ( (forall a b. (a -> b) -> Orville a -> Orville b)
-> (forall a b. a -> Orville b -> Orville a) -> Functor Orville
forall a b. a -> Orville b -> Orville a
forall a b. (a -> b) -> Orville a -> Orville b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> Orville a -> Orville b
fmap :: forall a b. (a -> b) -> Orville a -> Orville b
$c<$ :: forall a b. a -> Orville b -> Orville a
<$ :: forall a b. a -> Orville b -> Orville a
Functor
    , Functor Orville
Functor Orville
-> (forall a. a -> Orville a)
-> (forall a b. Orville (a -> b) -> Orville a -> Orville b)
-> (forall a b c.
    (a -> b -> c) -> Orville a -> Orville b -> Orville c)
-> (forall a b. Orville a -> Orville b -> Orville b)
-> (forall a b. Orville a -> Orville b -> Orville a)
-> Applicative Orville
forall a. a -> Orville a
forall a b. Orville a -> Orville b -> Orville a
forall a b. Orville a -> Orville b -> Orville b
forall a b. Orville (a -> b) -> Orville a -> Orville b
forall a b c. (a -> b -> c) -> Orville a -> Orville b -> Orville c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
$cpure :: forall a. a -> Orville a
pure :: forall a. a -> Orville a
$c<*> :: forall a b. Orville (a -> b) -> Orville a -> Orville b
<*> :: forall a b. Orville (a -> b) -> Orville a -> Orville b
$cliftA2 :: forall a b c. (a -> b -> c) -> Orville a -> Orville b -> Orville c
liftA2 :: forall a b c. (a -> b -> c) -> Orville a -> Orville b -> Orville c
$c*> :: forall a b. Orville a -> Orville b -> Orville b
*> :: forall a b. Orville a -> Orville b -> Orville b
$c<* :: forall a b. Orville a -> Orville b -> Orville a
<* :: forall a b. Orville a -> Orville b -> Orville a
Applicative
    , Applicative Orville
Applicative Orville
-> (forall a b. Orville a -> (a -> Orville b) -> Orville b)
-> (forall a b. Orville a -> Orville b -> Orville b)
-> (forall a. a -> Orville a)
-> Monad Orville
forall a. a -> Orville a
forall a b. Orville a -> Orville b -> Orville b
forall a b. Orville a -> (a -> Orville b) -> Orville b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
$c>>= :: forall a b. Orville a -> (a -> Orville b) -> Orville b
>>= :: forall a b. Orville a -> (a -> Orville b) -> Orville b
$c>> :: forall a b. Orville a -> Orville b -> Orville b
>> :: forall a b. Orville a -> Orville b -> Orville b
$creturn :: forall a. a -> Orville a
return :: forall a. a -> Orville a
Monad
    , Monad Orville
Monad Orville -> (forall a. IO a -> Orville a) -> MonadIO Orville
forall a. IO a -> Orville a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
$cliftIO :: forall a. IO a -> Orville a
liftIO :: forall a. IO a -> Orville a
MonadIO
    , (forall b.
 (forall a. (Connection -> IO a) -> IO a)
 -> (Connection -> Orville b) -> Orville b)
-> (forall e b.
    Exception e =>
    (forall a. IO a -> (e -> IO a) -> IO a)
    -> Orville b -> (e -> Orville b) -> Orville b)
-> (forall c.
    (forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b)
    -> ((forall a. Orville a -> Orville a) -> Orville c) -> Orville c)
-> MonadOrvilleControl Orville
forall b.
(forall a. (Connection -> IO a) -> IO a)
-> (Connection -> Orville b) -> Orville b
forall c.
(forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b)
-> ((forall a. Orville a -> Orville a) -> Orville c) -> Orville c
forall e b.
Exception e =>
(forall a. IO a -> (e -> IO a) -> IO a)
-> Orville b -> (e -> Orville b) -> Orville b
forall (m :: * -> *).
(forall b.
 (forall a. (Connection -> IO a) -> IO a)
 -> (Connection -> m b) -> m b)
-> (forall e b.
    Exception e =>
    (forall a. IO a -> (e -> IO a) -> IO a)
    -> m b -> (e -> m b) -> m b)
-> (forall c.
    (forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b)
    -> ((forall a. m a -> m a) -> m c) -> m c)
-> MonadOrvilleControl m
$cliftWithConnection :: forall b.
(forall a. (Connection -> IO a) -> IO a)
-> (Connection -> Orville b) -> Orville b
liftWithConnection :: forall b.
(forall a. (Connection -> IO a) -> IO a)
-> (Connection -> Orville b) -> Orville b
$cliftCatch :: forall e b.
Exception e =>
(forall a. IO a -> (e -> IO a) -> IO a)
-> Orville b -> (e -> Orville b) -> Orville b
liftCatch :: forall e b.
Exception e =>
(forall a. IO a -> (e -> IO a) -> IO a)
-> Orville b -> (e -> Orville b) -> Orville b
$cliftMask :: forall c.
(forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b)
-> ((forall a. Orville a -> Orville a) -> Orville c) -> Orville c
liftMask :: forall c.
(forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b)
-> ((forall a. Orville a -> Orville a) -> Orville c) -> Orville c
MonadOrville.MonadOrvilleControl
    , MonadIO Orville
HasOrvilleState Orville
MonadOrvilleControl Orville
HasOrvilleState Orville
-> MonadOrvilleControl Orville
-> MonadIO Orville
-> MonadOrville Orville
forall (m :: * -> *).
HasOrvilleState m
-> MonadOrvilleControl m -> MonadIO m -> MonadOrville m
MonadOrville.MonadOrville
    , Orville OrvilleState
Orville OrvilleState
-> (forall a.
    (OrvilleState -> OrvilleState) -> Orville a -> Orville a)
-> HasOrvilleState Orville
forall a. (OrvilleState -> OrvilleState) -> Orville a -> Orville a
forall (m :: * -> *).
m OrvilleState
-> (forall a. (OrvilleState -> OrvilleState) -> m a -> m a)
-> HasOrvilleState m
$caskOrvilleState :: Orville OrvilleState
askOrvilleState :: Orville OrvilleState
$clocalOrvilleState :: forall a. (OrvilleState -> OrvilleState) -> Orville a -> Orville a
localOrvilleState :: forall a. (OrvilleState -> OrvilleState) -> Orville a -> Orville a
HasOrvilleState.HasOrvilleState
    , Monad Orville
Monad Orville
-> (forall e a. Exception e => e -> Orville a)
-> MonadThrow Orville
forall e a. Exception e => e -> Orville a
forall (m :: * -> *).
Monad m -> (forall e a. Exception e => e -> m a) -> MonadThrow m
$cthrowM :: forall e a. Exception e => e -> Orville a
throwM :: forall e a. Exception e => e -> Orville a
ExSafe.MonadThrow
    , MonadThrow Orville
MonadThrow Orville
-> (forall e a.
    Exception e =>
    Orville a -> (e -> Orville a) -> Orville a)
-> MonadCatch Orville
forall e a.
Exception e =>
Orville a -> (e -> Orville a) -> Orville a
forall (m :: * -> *).
MonadThrow m
-> (forall e a. Exception e => m a -> (e -> m a) -> m a)
-> MonadCatch m
$ccatch :: forall e a.
Exception e =>
Orville a -> (e -> Orville a) -> Orville a
catch :: forall e a.
Exception e =>
Orville a -> (e -> Orville a) -> Orville a
ExSafe.MonadCatch
    )

{- |
  Runs an 'Orville' operation in the 'IO' monad using the given connection
  pool.

  This will run the 'Orville' operation with the
  'ErrorDetailLevel.ErrorDetailLevel' set to the default. If you want to run
  with a different detail level, you can use 'OrvilleState.newOrvilleState' to
  create a state with the desired detail level and then use
  'runOrvilleWithState'.

@since 1.0.0.0
-}
runOrville :: ConnectionPool -> Orville a -> IO a
runOrville :: forall a. ConnectionPool -> Orville a -> IO a
runOrville =
  OrvilleState -> Orville a -> IO a
forall a. OrvilleState -> Orville a -> IO a
runOrvilleWithState
    (OrvilleState -> Orville a -> IO a)
-> (ConnectionPool -> OrvilleState)
-> ConnectionPool
-> Orville a
-> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ErrorDetailLevel -> ConnectionPool -> OrvilleState
OrvilleState.newOrvilleState ErrorDetailLevel
ErrorDetailLevel.defaultErrorDetailLevel

{- |
  Runs an 'Orville' operation in the 'IO' monad, starting from the provided
  'OrvilleState.OrvilleState'.

  Caution: If you harvest an 'OrvilleState.OrvilleState' from inside a
  'MonadOrville.MonadOrville' monad using 'MonadOrville.askOrvilleState',
  you may pick up connection tracking state that you didn't intend to. You
  may want to use 'MonadOrville.resetOrvilleState' in this situation to get
  a new initial state before passing it to 'runOrvilleWithState'.

  On the other hand, if you know that you want to pass the existing connection
  state from another monad into the 'Orville' monad, this is how you do it.

@since 1.0.0.0
-}
runOrvilleWithState :: OrvilleState.OrvilleState -> Orville a -> IO a
runOrvilleWithState :: forall a. OrvilleState -> Orville a -> IO a
runOrvilleWithState OrvilleState
state Orville a
orville =
  ReaderT OrvilleState IO a -> OrvilleState -> IO a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (Orville a -> ReaderT OrvilleState IO a
forall a. Orville a -> ReaderT OrvilleState IO a
unwrapOrville Orville a
orville) OrvilleState
state