module Tendermint.SDK.BaseApp.Transaction.Cache
  ( Cache
  , emptyCache
  , writeCache
  , Deleted(..)
  , put
  , get
  , delete
  ) where

import           Data.ByteString                       (ByteString)
import           Data.Map.Strict                       (Map)
import qualified Data.Map.Strict                       as Map
import           Data.Set                              (Set)
import qualified Data.Set                              as Set
import           Polysemy                              (Member, Sem)
import           Polysemy.Tagged                       (Tagged, tag)
import           Tendermint.SDK.BaseApp.Store.RawStore (Scope (..), StoreKey,
                                                        WriteStore, storeDelete,
                                                        storePut)

data Cache = Cache
  { Cache -> Set StoreKey
keysToDelete :: Set StoreKey
  , Cache -> Map StoreKey ByteString
stateCache   :: Map StoreKey ByteString
  } deriving (Cache -> Cache -> Bool
(Cache -> Cache -> Bool) -> (Cache -> Cache -> Bool) -> Eq Cache
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Cache -> Cache -> Bool
$c/= :: Cache -> Cache -> Bool
== :: Cache -> Cache -> Bool
$c== :: Cache -> Cache -> Bool
Eq, Int -> Cache -> ShowS
[Cache] -> ShowS
Cache -> String
(Int -> Cache -> ShowS)
-> (Cache -> String) -> ([Cache] -> ShowS) -> Show Cache
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Cache] -> ShowS
$cshowList :: [Cache] -> ShowS
show :: Cache -> String
$cshow :: Cache -> String
showsPrec :: Int -> Cache -> ShowS
$cshowsPrec :: Int -> Cache -> ShowS
Show)

emptyCache :: Cache
emptyCache :: Cache
emptyCache = Set StoreKey -> Map StoreKey ByteString -> Cache
Cache Set StoreKey
forall a. Set a
Set.empty Map StoreKey ByteString
forall k a. Map k a
Map.empty

put
  :: StoreKey
  -> ByteString
  -> Cache
  -> Cache
put :: StoreKey -> ByteString -> Cache -> Cache
put k :: StoreKey
k v :: ByteString
v Cache{..} =
  let keysToDelete' :: Set StoreKey
keysToDelete' = StoreKey -> Set StoreKey -> Set StoreKey
forall a. Ord a => a -> Set a -> Set a
Set.delete StoreKey
k Set StoreKey
keysToDelete
      stateCache' :: Map StoreKey ByteString
stateCache' = StoreKey
-> ByteString -> Map StoreKey ByteString -> Map StoreKey ByteString
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert StoreKey
k ByteString
v Map StoreKey ByteString
stateCache
  in Set StoreKey -> Map StoreKey ByteString -> Cache
Cache Set StoreKey
keysToDelete' Map StoreKey ByteString
stateCache'

data Deleted = Deleted

get
  :: StoreKey
  -> Cache
  -> Either Deleted (Maybe ByteString)
get :: StoreKey -> Cache -> Either Deleted (Maybe ByteString)
get k :: StoreKey
k Cache{..} =
  if StoreKey
k StoreKey -> Set StoreKey -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set StoreKey
keysToDelete
    then Deleted -> Either Deleted (Maybe ByteString)
forall a b. a -> Either a b
Left Deleted
Deleted
    else Maybe ByteString -> Either Deleted (Maybe ByteString)
forall a b. b -> Either a b
Right (Maybe ByteString -> Either Deleted (Maybe ByteString))
-> Maybe ByteString -> Either Deleted (Maybe ByteString)
forall a b. (a -> b) -> a -> b
$ StoreKey -> Map StoreKey ByteString -> Maybe ByteString
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup StoreKey
k Map StoreKey ByteString
stateCache

delete
  :: StoreKey
  -> Cache
  -> Cache
delete :: StoreKey -> Cache -> Cache
delete k :: StoreKey
k Cache{..} =
  let keysToDelete' :: Set StoreKey
keysToDelete' = StoreKey -> Set StoreKey -> Set StoreKey
forall a. Ord a => a -> Set a -> Set a
Set.insert StoreKey
k Set StoreKey
keysToDelete
      stateCache' :: Map StoreKey ByteString
stateCache' = StoreKey -> Map StoreKey ByteString -> Map StoreKey ByteString
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete StoreKey
k Map StoreKey ByteString
stateCache
  in Set StoreKey -> Map StoreKey ByteString -> Cache
Cache Set StoreKey
keysToDelete' Map StoreKey ByteString
stateCache'

writeCache
  :: Member (Tagged 'Consensus WriteStore) r
  => Cache
  -> Sem r ()
writeCache :: Cache -> Sem r ()
writeCache Cache{..} = do
  ((StoreKey, ByteString) -> Sem r ())
-> [(StoreKey, ByteString)] -> Sem r ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (forall k1 (k2 :: k1) (e :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
Member (Tagged k2 e) r =>
Sem (e : r) a -> Sem r a
forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
Member (Tagged 'Consensus e) r =>
Sem (e : r) a -> Sem r a
tag @'Consensus (Sem (Any : r) () -> Sem r ())
-> ((StoreKey, ByteString) -> Sem (Any : r) ())
-> (StoreKey, ByteString)
-> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (StoreKey -> ByteString -> Sem (Any : r) ())
-> (StoreKey, ByteString) -> Sem (Any : r) ()
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry StoreKey -> ByteString -> Sem (Any : r) ()
forall (r :: [(* -> *) -> * -> *]).
MemberWithError WriteStore r =>
StoreKey -> ByteString -> Sem r ()
storePut) (Map StoreKey ByteString -> [(StoreKey, ByteString)]
forall k a. Map k a -> [(k, a)]
Map.toList Map StoreKey ByteString
stateCache)
  (StoreKey -> Sem r ()) -> [StoreKey] -> Sem r ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (forall k1 (k2 :: k1) (e :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
Member (Tagged k2 e) r =>
Sem (e : r) a -> Sem r a
forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
Member (Tagged 'Consensus e) r =>
Sem (e : r) a -> Sem r a
tag @'Consensus (Sem (Any : r) () -> Sem r ())
-> (StoreKey -> Sem (Any : r) ()) -> StoreKey -> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StoreKey -> Sem (Any : r) ()
forall (r :: [(* -> *) -> * -> *]).
MemberWithError WriteStore r =>
StoreKey -> Sem r ()
storeDelete) (Set StoreKey -> [StoreKey]
forall a. Set a -> [a]
Set.toList Set StoreKey
keysToDelete)