keyed-vals-mem-0.1.0.0: src/KeyedVals/Handle/Mem.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_HADDOCK prune not-home #-}
{- |
Copyright : (c) 2018-2022 Tim Emiola
SPDX-License-Identifier: BSD3
Maintainer : Tim Emiola <tim@emio.la>
Provides an in-memory 'Handle' implementation.
-}
module KeyedVals.Handle.Mem (
-- * functions
new,
-- * module re-exports
module KeyedVals.Handle,
) where
import Control.Monad.IO.Unlift (MonadIO, MonadUnliftIO, liftIO)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import KeyedVals.Handle
import KeyedVals.Handle.Internal (Handle (..))
import Numeric.Natural (Natural)
import UnliftIO.STM (
STM,
TVar,
atomically,
newTVarIO,
readTVar,
writeTVar,
)
-- | Create a new 'Handle'.
new :: MonadUnliftIO m => m (Handle m)
new = do
v <- liftIO $ newTVarIO (mempty, False)
pure $
Handle
{ hLoadVal = hLoadVal' v
, hSaveVal = hSaveVal' v
, hCountKVs = hCountKVs' v
, hLoadKVs = hLoadKVs' v
, hSaveKVs = hSaveKVs' v
, hUpdateKVs = hUpdateKVs' v
, hLoadFrom = hLoadFrom' v
, hSaveTo = hSaveTo' v
, hLoadSlice = hLoadSlice' v
, hDeleteSelected = hDeleteSelected' v
, hDeleteSelectedKVs = hDeleteSelectedKVs' v
, hClose = hClose' v
}
-- | Implement an in-memory 'Handle'.
type InMem = Map Key InMemValue
-- | Store an in-memory 'Handle'.
type InMemVar = TVar (InMem, Bool)
-- | InMemValue represents a value to be stored in an in-memory 'Handle'.
data InMemValue
= Dict !ValsByKey
| Simple !Val
hClose' :: MonadUnliftIO m => InMemVar -> m ()
hClose' var = liftIO $
atomically $ do
(fh, _) <- readTVar var
writeTVar var (fh, True)
hLoadVal' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
m (Either HandleErr (Maybe Val))
hLoadVal' var key = withInMemKey var key $ \case
Nothing -> pure $ Right Nothing
Just (Dict _) -> pure $ Left BadKey
Just (Simple v) -> pure $ Right $ Just v
hSaveVal' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
Val ->
m (Either HandleErr ())
hSaveVal' var key value = withInMem' var $ \values -> do
updateInMem var $ Map.insert key (Simple value) values
hLoadKVs' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
m (Either HandleErr ValsByKey)
hLoadKVs' var key = withInMemKey var key $ \case
Nothing -> pure $ Right Map.empty
Just (Dict v) -> pure $ Right v
Just (Simple _) -> pure $ Left BadKey
hLoadSlice' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
Selection ->
m (Either HandleErr ValsByKey)
hLoadSlice' var key sel = withInMemKey var key $ \case
Nothing -> pure $ Right Map.empty
Just (Dict v) -> pure $ Right $ Map.filterWithKey (predOf sel) v
Just (Simple _) -> pure $ Left BadKey
hCountKVs' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
m (Either HandleErr Natural)
hCountKVs' var key = withInMemKey var key $ \case
Nothing -> pure $ Right 0
Just (Dict v) -> pure $ Right $ fromInteger $ toInteger $ Map.size v
Just (Simple _) -> pure $ Left BadKey
hSaveKVs' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
ValsByKey ->
m (Either HandleErr ())
hSaveKVs' var key d = withInMem' var $ \values -> do
updateInMem var $ Map.insert key (Dict d) values
hUpdateKVs' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
ValsByKey ->
m (Either HandleErr ())
hUpdateKVs' var key d = withInMem' var $ \values ->
case Map.lookup key values of
Nothing -> do
updateInMem var $ Map.insert key (Dict d) values
Just (Dict d') -> do
updateInMem var $ Map.insert key (Dict $ Map.union d d') values
Just (Simple _) -> pure $ Left BadKey
updateInMem :: TVar (a, Bool) -> a -> STM (Either err ())
updateInMem var newMap = do
writeTVar var (newMap, False)
pure $ Right ()
hSaveTo' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
Key ->
Val ->
m (Either HandleErr ())
hSaveTo' var key dictKey value = withInMem' var $ \values ->
case Map.lookup key values of
Nothing -> do
updateInMem var $ Map.insert key (Dict $ Map.singleton dictKey value) values
Just (Dict d) -> do
updateInMem var $ Map.insert key (Dict $ Map.insert dictKey value d) values
Just (Simple _) -> pure $ Left BadKey
hLoadFrom' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
Key ->
m (Either HandleErr (Maybe Val))
hLoadFrom' var key dictKey = withInMemKey var key $ \case
Nothing -> pure $ Right Nothing
Just (Dict d) -> pure $ Right $ Map.lookup dictKey d
Just (Simple _) -> pure $ Left BadKey
hDeleteSelectedKVs' ::
MonadUnliftIO m =>
InMemVar ->
Key ->
Selection ->
m (Either HandleErr ())
hDeleteSelectedKVs' var key sel = withInMem' var $ \values ->
case Map.lookup key values of
Nothing -> pure $ Right ()
Just (Dict d) -> do
writeTVar var (Map.insert key (Dict (Map.filterWithKey (notSel sel) d)) values, False)
pure $ Right ()
Just (Simple _) -> pure $ Left BadKey
hDeleteSelected' ::
MonadUnliftIO m =>
InMemVar ->
Selection ->
m (Either HandleErr ())
hDeleteSelected' var sel = withInMem' var $ \values -> do
writeTVar var (Map.filterWithKey (notSel sel) values, False)
pure $ Right ()
notSel :: Selection -> Key -> p -> Bool
notSel s k _ = not $ k `isIn` s
predOf :: Selection -> Key -> p -> Bool
predOf s k _ = k `isIn` s
withInMem :: MonadIO m => TVar t -> (t -> STM a) -> m a
withInMem v f = liftIO $ atomically $ readTVar v >>= f
withInMem' ::
MonadUnliftIO m =>
InMemVar ->
(InMem -> STM (Either HandleErr a)) ->
m (Either HandleErr a)
withInMem' var f = withInMem var $ \case
(_, True) -> pure $ Left ConnectionClosed
(values, _) -> f values
withInMemKey ::
MonadUnliftIO m =>
InMemVar ->
Key ->
(Maybe InMemValue -> STM (Either HandleErr a)) ->
m (Either HandleErr a)
withInMemKey var key f = withInMem var $ \case
(_, True) -> pure $ Left ConnectionClosed
(values, _) -> f (Map.lookup key values)