packages feed

grapesy-1.0.0: kvstore/KVStore/Util/Store.hs

-- | Simple in-memory key-value var
--
-- Intended for qualified import.
--
-- > import KVStore.Util.Store (Store)
-- > import KVStore.Util.Store qualified as Store
module KVStore.Util.Store (
    Store -- opaque
    -- * API
  , new
  , get
  , putIfAbsent
  , replace
  , remove
  ) where

import Control.Concurrent
import Data.Hashable (Hashable)
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HashMap

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

newtype Store a b = Wrap {
      unwrap :: MVar (HashMap a b)
    }

{-------------------------------------------------------------------------------
  API
-------------------------------------------------------------------------------}

new :: IO (Store a b)
new = Wrap <$> newMVar HashMap.empty

-- | Get value associated with the key, if it exists
get :: Hashable a => Store a b -> a -> IO (Maybe b)
get store key = withStore store $ HashMap.lookup key

-- | Remove an key from the map
remove :: Hashable a => Store a b -> a -> IO ()
remove store key = modifyStore_ store $ HashMap.delete key

-- | Put a new key/value pair into the var
--
-- Returns 'True' on success, or 'False' if they key was already present.
putIfAbsent :: Hashable a => Store a b -> a -> b -> IO Bool
putIfAbsent store key value =
    modifyStore store $ \hm ->
      case HashMap.lookup key hm of
        Nothing -> (HashMap.insert key value hm, True)
        Just _  -> (hm, False)

-- | Replace the value associated with the key, if the key already exists
--
-- Returns 'True' if the value was successfully replaced, or 'False' if the key
-- was not present.
replace :: Hashable a => Store a b -> a -> b -> IO Bool
replace store key newValue =
    modifyStore store $ \hm ->
      case HashMap.lookup key hm of
        Nothing -> (hm, False)
        Just _  -> (HashMap.insert key newValue hm, True)

{-------------------------------------------------------------------------------
  Internal: wrap pure operations
-------------------------------------------------------------------------------}

withStore :: Store a b -> (HashMap a b -> r) -> IO r
withStore store f = withMVar (unwrap store) $ return . f

modifyStore :: Store a b -> (HashMap a b -> (HashMap a b, r)) -> IO r
modifyStore store f = modifyMVar (unwrap store) $ return . f

modifyStore_ :: Store a b -> (HashMap a b -> HashMap a b) -> IO ()
modifyStore_ store f = modifyMVar_ (unwrap store) $ return . f