{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Polysemy.JSONFileKVStore where
import Control.Monad.Catch
import Control.Monad.Extra
import Control.Monad.IO.Class
import Data.Aeson
import qualified Data.Map as Map
import Data.Map (Map)
import GHC.Generics
import Polysemy
import Polysemy.Error
import Polysemy.Input
import Polysemy.KVStore
import Path
import qualified UnliftIO.Path.Directory as U
newtype JSONParseException = JSONParseException String
deriving (Show, Eq, Generic)
instance Exception JSONParseException where
displayException (JSONParseException x) = x
eitherDecodeOrCreate :: (ToJSON a, FromJSON a, MonadIO m) => Path b File -> a -> m (Either String a)
eitherDecodeOrCreate f x = do
whenM (fmap not . U.doesFileExist $ f) $ liftIO $ encodeFile (toFilePath f) x
liftIO $ eitherDecodeFileStrict' (toFilePath f)
runKVStoreAsJSONFileStore :: (Members '[Embed IO, Error JSONParseException] r,
FromJSONKey k, ToJSONKey k, FromJSON v, ToJSON v, Ord k)
=> Path b File
-> Sem (KVStore k v ': r) a -> Sem r a
runKVStoreAsJSONFileStore d = interpret \case
LookupKV k -> do
z <- eitherDecodeOrCreate d mempty
case z of
Left x -> throw @JSONParseException $ JSONParseException x
Right x -> return $ Map.lookup k x
UpdateKV k v -> do
z <- eitherDecodeOrCreate d mempty
case z of
Left x -> throw $ JSONParseException x
Right (x :: Map k v) -> embed $ encodeFile (toFilePath d) (Map.alter (const v) k x)