module Hasql.PreparedStatementRegistry.Map
  ( -- * Pure registry operations
    RegistryState,
    empty,
    lookup,
    insert,
    reset,

    -- * Key type
    LocalKey (..),
  )
where

import ByteString.StrictBuilder qualified as B
import Data.HashMap.Strict qualified as HashMap
import Hasql.LibPq14 qualified as Pq
import Hasql.Prelude hiding (empty, insert, lookup, reset)

-- | Pure registry state containing the hash map and counter
data RegistryState = RegistryState (HashMap.HashMap LocalKey ByteString) Word

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

-- | Pure lookup operation
{-# INLINEABLE lookup #-}
lookup :: LocalKey -> RegistryState -> Maybe ByteString
lookup :: LocalKey -> RegistryState -> Maybe ByteString
lookup LocalKey
localKey (RegistryState 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

-- | Pure insert operation that returns new state and the generated remote key
{-# INLINEABLE insert #-}
insert :: LocalKey -> RegistryState -> (ByteString, RegistryState)
insert :: LocalKey -> RegistryState -> (ByteString, RegistryState)
insert LocalKey
localKey (RegistryState HashMap LocalKey ByteString
hashMap Word
counter) = (ByteString
remoteKey, RegistryState
newState)
  where
    remoteKey :: ByteString
remoteKey = Builder -> ByteString
B.builderBytes (Builder -> ByteString) -> (Word -> Builder) -> Word -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Word -> Builder
forall a. Integral a => a -> Builder
B.asciiIntegral (Word -> ByteString) -> Word -> ByteString
forall a b. (a -> b) -> a -> b
$ Word
counter
    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 -> Word
forall a. Enum a => a -> a
succ Word
counter
    newState :: RegistryState
newState = HashMap LocalKey ByteString -> Word -> RegistryState
RegistryState HashMap LocalKey ByteString
newHashMap Word
newCounter

-- | Pure reset operation
{-# INLINEABLE reset #-}
reset :: RegistryState -> RegistryState
reset :: RegistryState -> RegistryState
reset RegistryState
_ = HashMap LocalKey ByteString -> Word -> RegistryState
RegistryState 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]
_) =
    Int -> ByteString -> Int
forall a. Hashable a => Int -> a -> Int
hashWithSalt Int
salt ByteString
template