packages feed

polysemy-hasql-0.0.1.0: lib/Polysemy/Hasql/Transaction.hs

module Polysemy.Hasql.Transaction where

import Hasql.Connection (Connection)
import Polysemy.Db.Effect.Store (QStore, Store)
import Sqel.Data.Uid (Uid)

import qualified Polysemy.Hasql.Effect.Database as Database
import Polysemy.Hasql.Effect.Database (ConnectionSource)
import qualified Polysemy.Hasql.Effect.Transaction as Transaction
import Polysemy.Hasql.Effect.Transaction (Transaction, Transactions)
import Polysemy.Internal.CustomErrors (FirstOrder)

type XaQStore res err f q d =
  Scoped res (QStore f q d !! err) !! err

type XaStore res err i d =
  XaQStore res err Maybe i (Uid i d)

type TransactionEffects :: EffectRow -> EffectRow -> Type -> Type -> EffectRow -> Constraint
class TransactionEffects all effs err res r where
  transactionEffects :: res -> InterpretersFor effs r

instance TransactionEffects all '[] err res r where
  transactionEffects _ = id

instance (
    TransactionEffects all effs err res r,
    Member (Scoped res (eff !! err) !! err) (effs ++ r),
    Member (Stop err) (effs ++ r)
  ) => TransactionEffects all (eff : effs) err res r where
  transactionEffects res =
    transactionEffects @all @effs @err res .
    restop @err @(Scoped res (eff !! err)) .
    scoped res .
    restop @err @eff .
    raiseUnder2

-- TODO add scope parameter of type TransactionConfig
transact ::
  ∀ effs res err r .
  effs ++ '[Transaction res] ++ r ~ (effs ++ '[Transaction res]) ++ r =>
  Member (Transactions res) r =>
  TransactionEffects effs effs err res (Transaction res : r) =>
  InterpretersFor (effs ++ '[Transaction res]) r
transact ma =
  scoped_ do
    conn <- Transaction.resource
    transactionEffects @effs @effs @err conn do
      ma

type family XaStores (ds :: [(Type, Type)]) :: [Effect] where
  XaStores '[] = '[]
  XaStores ('(i, d) : ds) = Store i d : XaStores ds

transactStores ::
  ∀ ds res err r xas .
  xas ~ XaStores ds =>
  XaStores ds ++ (Transaction res : r) ~ (xas ++ '[Transaction res]) ++ r =>
  Member (Transactions res) r =>
  TransactionEffects xas xas err res (Transaction res : r) =>
  InterpretersFor (xas ++ '[Transaction res]) r
transactStores ma =
  scoped_ do
    conn <- Transaction.resource
    transactionEffects @xas @xas @err conn do
      ma

connectionScope ::
  ∀ eff err r a .
  Member (Scoped ConnectionSource (eff !! err)) r =>
  Connection ->
  (() -> Sem (eff !! err : r) a) ->
  Sem r a
connectionScope conn use =
  scoped (Database.Supplied "transaction" conn) (use ())

interpretForXa ::
  ∀ dep eff err r .
  Member (Scoped ConnectionSource (dep !! err)) r =>
  (∀ x r0 . eff (Sem r0) x -> Sem (Stop err : dep !! err : r) x) ->
  InterpreterFor (Scoped Connection (eff !! err) !! err) r
interpretForXa handler =
  interpretScopedRWith @'[dep !! err] connectionScope \ () -> insertAt @2 . handler

interpretWithXa ::
  ∀ dep eff err r .
  FirstOrder eff "interpretResumable" =>
  Members [Scoped ConnectionSource (dep !! err), dep !! err] r =>
  (∀ x r0 . eff (Sem r0) x -> Sem (Stop err : dep !! err : r) x) ->
  InterpretersFor [Scoped Connection (eff !! err) !! err, eff !! err] r
interpretWithXa handler =
  interpretResumable (subsume_ . raise2Under . handler) .
  interpretForXa (raise2Under . handler)