module Hasql.Engine.Structures.StatementCache
  ( -- * Pure registry operations
    StatementCache,
    empty,
    lookup,
    insert,
    reset,
  )
where

import Data.HashMap.Strict qualified as HashMap
import Hasql.Platform.Prelude hiding (empty, insert, lookup, reset)
import Hasql.Pq qualified as Pq

-- | Pure registry state containing the hash map and counter
data StatementCache = StatementCache (HashMap LocalKey ByteString) Word
  deriving stock (Int -> StatementCache -> ShowS
[StatementCache] -> ShowS
StatementCache -> String
(Int -> StatementCache -> ShowS)
-> (StatementCache -> String)
-> ([StatementCache] -> ShowS)
-> Show StatementCache
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> StatementCache -> ShowS
showsPrec :: Int -> StatementCache -> ShowS
$cshow :: StatementCache -> String
show :: StatementCache -> String
$cshowList :: [StatementCache] -> ShowS
showList :: [StatementCache] -> ShowS
Show, StatementCache -> StatementCache -> Bool
(StatementCache -> StatementCache -> Bool)
-> (StatementCache -> StatementCache -> Bool) -> Eq StatementCache
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: StatementCache -> StatementCache -> Bool
== :: StatementCache -> StatementCache -> Bool
$c/= :: StatementCache -> StatementCache -> Bool
/= :: StatementCache -> StatementCache -> Bool
Eq)

-- | Create an empty registry state
{-# INLINEABLE empty #-}
empty :: StatementCache
empty :: StatementCache
empty = HashMap LocalKey ByteString -> Word -> StatementCache
StatementCache HashMap LocalKey ByteString
forall k v. HashMap k v
HashMap.empty Word
0

-- | Pure lookup operation
{-# INLINEABLE lookup #-}
lookup :: ByteString -> [Pq.Oid] -> StatementCache -> Maybe ByteString
lookup :: ByteString -> [Oid] -> StatementCache -> Maybe ByteString
lookup ByteString
sql [Oid]
oids (StatementCache HashMap LocalKey ByteString
hashMap Word
_) = LocalKey -> HashMap LocalKey ByteString -> Maybe ByteString
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup LocalKey
localKey HashMap LocalKey ByteString
hashMap
  where
    localKey :: LocalKey
localKey = ByteString -> [Oid] -> LocalKey
LocalKey ByteString
sql [Oid]
oids

-- | Pure insert operation that returns new state and the generated remote key
{-# INLINEABLE insert #-}
insert :: ByteString -> [Pq.Oid] -> StatementCache -> (ByteString, StatementCache)
insert :: ByteString
-> [Oid] -> StatementCache -> (ByteString, StatementCache)
insert ByteString
sql [Oid]
oids (StatementCache HashMap LocalKey ByteString
hashMap Word
counter) = (ByteString
remoteKey, StatementCache
newState)
  where
    remoteKey :: ByteString
remoteKey = String -> ByteString
forall a. IsString a => String -> a
fromString (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ Word -> String
forall a. Show a => a -> String
show (Word -> String) -> Word -> String
forall a b. (a -> b) -> a -> b
$ Word
newCounter
    newHashMap :: HashMap LocalKey ByteString
newHashMap = LocalKey
-> ByteString
-> HashMap LocalKey ByteString
-> HashMap LocalKey ByteString
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HashMap.insert LocalKey
localKey ByteString
remoteKey HashMap LocalKey ByteString
hashMap
    newCounter :: Word
newCounter = Word
counter Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
1
    newState :: StatementCache
newState = HashMap LocalKey ByteString -> Word -> StatementCache
StatementCache HashMap LocalKey ByteString
newHashMap Word
newCounter
    localKey :: LocalKey
localKey = ByteString -> [Oid] -> LocalKey
LocalKey ByteString
sql [Oid]
oids

-- | Pure reset operation
{-# INLINEABLE reset #-}
reset :: StatementCache -> StatementCache
reset :: StatementCache -> StatementCache
reset StatementCache
_ = HashMap LocalKey ByteString -> Word -> StatementCache
StatementCache HashMap LocalKey ByteString
forall k v. HashMap k v
HashMap.empty Word
0

-- |
-- Local statement key.
data LocalKey
  = LocalKey ByteString [Pq.Oid]
  deriving (Int -> LocalKey -> ShowS
[LocalKey] -> ShowS
LocalKey -> String
(Int -> LocalKey -> ShowS)
-> (LocalKey -> String) -> ([LocalKey] -> ShowS) -> Show LocalKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> LocalKey -> ShowS
showsPrec :: Int -> LocalKey -> ShowS
$cshow :: LocalKey -> String
show :: LocalKey -> String
$cshowList :: [LocalKey] -> ShowS
showList :: [LocalKey] -> ShowS
Show, LocalKey -> LocalKey -> Bool
(LocalKey -> LocalKey -> Bool)
-> (LocalKey -> LocalKey -> Bool) -> Eq LocalKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: LocalKey -> LocalKey -> Bool
== :: LocalKey -> LocalKey -> Bool
$c/= :: LocalKey -> LocalKey -> Bool
/= :: LocalKey -> LocalKey -> Bool
Eq)

instance Hashable LocalKey where
  {-# INLINE hashWithSalt #-}
  hashWithSalt :: Int -> LocalKey -> Int
hashWithSalt Int
salt (LocalKey ByteString
template [Oid]
oids) =
    Int -> [Word32] -> Int
forall a. Hashable a => Int -> a -> Int
hashWithSalt (Int -> ByteString -> Int
forall a. Hashable a => Int -> a -> Int
hashWithSalt Int
salt ByteString
template) ((Oid -> Word32) -> [Oid] -> [Word32]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Oid -> Word32
Pq.oidToWord32 [Oid]
oids)