packages feed

bluefin-postgresql-0.1.0.0: src/Bluefin/PostgreSQL/Connection.hs

module Bluefin.PostgreSQL.Connection
  ( -- * Effect
    WithConnection (..)
  , withConnection

    -- * Interpret with a single Connection
  , runWithConnection
  , runWithConnectInfo
  )
where

import Bluefin.Compound
import Bluefin.Eff
import Bluefin.IO
import qualified Database.PostgreSQL.Simple as PSQL
import GHC.Stack

{- | A dynamic effect that lets us use a database 'PSQL.Connection',
without specifying __how__ that connection is supplied.

For example, we might want to just provide a connection and let the interpreter
use that for the whole procedure (see 'Bluefin.PostgreSQL.Connection.runWithConnection').

Or, we might want to create a connection pool which provides connections when
the interpreter asks for them (see "Bluefin.PostgreSQL.Connection.Pool").
-}
newtype WithConnection (e :: Effects) = MkWithConnection
  { withConnectionImpl :: forall e' a. (PSQL.Connection -> Eff e' a) -> Eff (e' :& e) a
  -- ^ Use a 'PSQL.Connection' provided by an interpreter.
  }

instance Handle WithConnection where
  mapHandle h =
    MkWithConnection
      { withConnectionImpl = useImplUnder . withConnectionImpl h
      }

-- | Use a 'PSQL.Connection' provided by an interpreter.
withConnection :: (e :> es) => WithConnection e -> (PSQL.Connection -> Eff es a) -> Eff es a
withConnection w f = makeOp (withConnectionImpl (mapHandle w) f)

{- | Run a t'WithConnection' effect by simply supplying a 'PSQL.Connection'.
The connection will be kept alive for the whole duration of the procedure,
which might not be want you want for long-running processes. If so, see
"Bluefin.PostgreSQL.Connection.Pool".
-}
runWithConnection ::
  PSQL.Connection ->
  (forall e. WithConnection e -> Eff (e :& es) a) ->
  Eff es a
runWithConnection conn k =
  useImplIn
    k
    MkWithConnection
      { withConnectionImpl = \f -> useImpl (f conn)
      }

{- | Run a t'WithConnection' effect using a 'PSQL.ConnectInfo'.
The 'PSQL.ConnectInfo' will be used to create a 'PSQL.Connection' which will be
kept alive for the whole duration of the procedure, which might not be want you want
for long-running processes. If so, see "Bluefin.PostgreSQL.Connection.Pool".

'PSQL.withConnect' will handle opening and closing the 'PSQL.Connection'.
-}
runWithConnectInfo ::
  (HasCallStack, e1 :> es) =>
  IOE e1 ->
  PSQL.ConnectInfo ->
  (forall e. WithConnection e -> Eff (e :& es) a) ->
  Eff es a
runWithConnectInfo ioe connInfo k =
  withEffToIO_ ioe $ \unlift ->
    PSQL.withConnect connInfo $ \conn ->
      unlift $ runWithConnection conn k