module Polysemy.Hasql.Interpreter.Transaction where

import Hasql.Connection (Connection)
import qualified Polysemy.Db.Data.DbError as DbError
import Polysemy.Db.Data.DbError (DbError)

import qualified Polysemy.Hasql.Effect.Database as Database
import Polysemy.Hasql.Effect.Database (ConnectionSource, Database, withDatabaseUnique)
import Polysemy.Hasql.Effect.Transaction (Transaction (Abort, Resource), Transactions)
import Polysemy.Hasql.Statement.Transaction (beginTransaction, commitTransaction, rollbackTransaction)

transactionScopeWithConnection ::
  Members [Database, Resource, Stop DbError] r =>
  (Connection -> Sem (Stop DbError : r) a) ->
  Sem r a
transactionScopeWithConnection :: forall (r :: EffectRow) a.
Members '[Database, Resource, Stop DbError] r =>
(Connection -> Sem (Stop DbError : r) a) -> Sem r a
transactionScopeWithConnection Connection -> Sem (Stop DbError : r) a
use =
  forall (r :: EffectRow) a c b.
Member Resource r =>
Sem r a -> (a -> Sem r c) -> (a -> Sem r b) -> Sem r b
bracketOnError (forall {o}. Statement () o -> Sem r o
runS (TransactionConfig -> Bool -> Statement () ()
beginTransaction forall a. Default a => a
def Bool
False)) () -> Sem r ()
abortTr \ () -> do
    forall (r :: EffectRow) a.
Member Database r =>
(Connection -> Sem r a) -> Sem r a
Database.use \ Connection
connection ->
      forall (e :: (* -> *) -> * -> *) (r :: EffectRow) a.
Member e r =>
Sem (e : r) a -> Sem r a
subsume (Connection -> Sem (Stop DbError : r) a
use Connection
connection) forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall {o}. Statement () o -> Sem r o
runS (Bool -> Statement () ()
commitTransaction Bool
False)
  where
    abortTr :: () -> Sem r ()
abortTr ()
_ = forall {o}. Statement () o -> Sem r o
runS (Bool -> Statement () ()
rollbackTransaction Bool
False)
    runS :: Statement () o -> Sem r o
runS = forall (r :: EffectRow) p o.
Member Database r =>
p -> Statement p o -> Sem r o
Database.statement ()

transactionScope ::
  Members [Scoped ConnectionSource (Database !! DbError), Resource] (Stop DbError : r) =>
  (Connection -> Sem (Stop DbError : r) a) ->
  Sem (Stop DbError : r) a
transactionScope :: forall (r :: EffectRow) a.
Members
  '[Scoped ConnectionSource (Database !! DbError), Resource]
  (Stop DbError : r) =>
(Connection -> Sem (Stop DbError : r) a)
-> Sem (Stop DbError : r) a
transactionScope Connection -> Sem (Stop DbError : r) a
use =
  forall (r :: EffectRow).
Member (Scoped ConnectionSource (Database !! DbError)) r =>
Maybe ConnectionTag -> InterpreterFor (Database !! DbError) r
withDatabaseUnique forall a. Maybe a
Nothing forall a b. (a -> b) -> a -> b
$ forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow).
Members '[Resumable err eff, Stop err] r =>
InterpreterFor eff r
restop @DbError @Database do
    forall (r :: EffectRow) a.
Members '[Database, Resource, Stop DbError] r =>
(Connection -> Sem (Stop DbError : r) a) -> Sem r a
transactionScopeWithConnection (forall (index :: Nat) (inserted :: EffectRow) (head :: EffectRow)
       (oldTail :: EffectRow) (tail :: EffectRow) (old :: EffectRow)
       (full :: EffectRow) a.
(ListOfLength index head, WhenStuck index InsertAtUnprovidedIndex,
 old ~ Append head oldTail, tail ~ Append inserted oldTail,
 full ~ Append head tail,
 InsertAtIndex index head tail oldTail full inserted) =>
Sem old a -> Sem full a
insertAt @1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. Connection -> Sem (Stop DbError : r) a
use)

interpretTransactions ::
  Members [Scoped ConnectionSource (Database !! DbError), Resource] r =>
  InterpreterFor (Transactions Connection !! DbError) r
interpretTransactions :: forall (r :: EffectRow).
Members
  '[Scoped ConnectionSource (Database !! DbError), Resource] r =>
InterpreterFor (Transactions Connection !! DbError) r
interpretTransactions =
  forall (extra :: EffectRow) param resource
       (effect :: (* -> *) -> * -> *) err (r :: EffectRow).
KnownList extra =>
(forall (q :: (* -> *) -> * -> *) x.
 param
 -> (resource -> Sem (extra ++ (Stop err : Opaque q : r)) x)
 -> Sem (Stop err : Opaque q : r) x)
-> (forall (q :: (* -> *) -> * -> *) (r0 :: EffectRow) x.
    resource
    -> effect (Sem r0) x
    -> Tactical
         effect (Sem r0) (extra ++ ('[Stop err, Opaque q] ++ r)) x)
-> InterpreterFor (Scoped param effect !! err) r
interpretScopedResumableWithH @'[] (forall a b. a -> b -> a
const forall (r :: EffectRow) a.
Members
  '[Scoped ConnectionSource (Database !! DbError), Resource]
  (Stop DbError : r) =>
(Connection -> Sem (Stop DbError : r) a)
-> Sem (Stop DbError : r) a
transactionScope) \ Connection
conn -> \case
    Transaction Connection (Sem r0) x
Resource ->
      forall (f :: * -> *) a (e :: (* -> *) -> * -> *) (m :: * -> *)
       (r :: EffectRow).
Functor f =>
a -> Sem (WithTactics e f m r) (f a)
pureT Connection
conn
    Transaction Connection (Sem r0) x
Abort ->
      forall e (r :: EffectRow) a. Member (Stop e) r => e -> Sem r a
stop (Text -> DbError
DbError.Query Text
"aborted by user")