{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Imm.Database where
import Imm.Logger hiding (Handle)
import qualified Imm.Logger as Logger
import Imm.Pretty
import Control.Exception.Safe hiding (handle)
import Data.Map (Map)
class (Ord (Key t), Show (Key t), Show (Entry t), Typeable t, Show t, Pretty t, Pretty (Key t))
=> Table t where
type Key t :: *
type Entry t :: *
rep :: t
data Handle m t = Handle
{ _describeDatabase :: forall a . m (Doc a)
, _fetchList :: [Key t] -> m (Map (Key t) (Entry t))
, _fetchAll :: m (Map (Key t) (Entry t))
, _update :: Key t -> (Entry t -> Entry t) -> m ()
, _insertList :: [(Key t, Entry t)] -> m ()
, _deleteList :: [Key t] -> m ()
, _purge :: m ()
, _commit :: m ()
}
data DatabaseException t
= NotCommitted t
| NotDeleted t [Key t]
| NotFound t [Key t]
| NotInserted t [(Key t, Entry t)]
| NotPurged t
| NotUpdated t (Key t)
| UnableFetchAll t
deriving instance (Eq t, Eq (Key t), Eq (Entry t)) => Eq (DatabaseException t)
deriving instance (Show t, Show (Key t), Show (Entry t)) => Show (DatabaseException t)
instance (Table t, Show (Key t), Show (Entry t), Pretty (Key t), Typeable t) => Exception (DatabaseException t) where
displayException = show . pretty
instance (Pretty t, Pretty (Key t)) => Pretty (DatabaseException t) where
pretty (NotCommitted _) = "Unable to commit database changes."
pretty (NotDeleted _ x) = "Unable to delete the following entries in database:" <++> indent 2 (vsep $ map pretty x)
pretty (NotFound _ x) = "Unable to find the following entries in database:" <++> indent 2 (vsep $ map pretty x)
pretty (NotInserted _ x) = "Unable to insert the following entries in database:" <++> indent 2 (vsep $ map (pretty . fst) x)
pretty (NotPurged t) = "Unable to purge database" <+> pretty t
pretty (NotUpdated _ x) = "Unable to update the following entry in database:" <++> indent 2 (pretty x)
pretty (UnableFetchAll _) = "Unable to fetch all entries from database."
fetch :: Monad m => Table t => MonadThrow m => Handle m t -> Key t -> m (Entry t)
fetch handle k = do
results <- _fetchList handle [k]
maybe (throwM $ NotFound (table handle) [k]) return $ lookup k results
fetchList :: Monad m => Handle m t -> [Key t] -> m (Map (Key t) (Entry t))
fetchList = _fetchList
fetchAll :: Monad m => Handle m t -> m (Map (Key t) (Entry t))
fetchAll = _fetchAll
update :: Monad m => Handle m t -> Key t -> (Entry t -> Entry t) -> m ()
update = _update
insert :: Monad m => Logger.Handle m -> Handle m t -> Key t -> Entry t -> m ()
insert logger handle k v = insertList logger handle [(k, v)]
insertList :: Monad m => Logger.Handle m -> Handle m t -> [(Key t, Entry t)] -> m ()
insertList logger handle i = do
log logger Info $ "Inserting " <> yellow (pretty $ length i) <> " entries..."
_insertList handle i
delete :: Monad m => Logger.Handle m -> Handle m t -> Key t -> m ()
delete logger handle k = deleteList logger handle [k]
deleteList :: Monad m => Logger.Handle m -> Handle m t -> [Key t] -> m ()
deleteList logger handle k = do
log logger Info $ "Deleting " <> yellow (pretty $ length k) <> " entries..."
_deleteList handle k
purge :: Monad m => Logger.Handle m -> Handle m t -> m ()
purge logger handle = do
log logger Info "Purging database..."
_purge handle
commit :: Monad m => Logger.Handle m -> Handle m t -> m ()
commit logger handle = do
log logger Debug "Committing database transaction..."
_commit handle
log logger Debug "Database transaction committed"
table :: Table t => Handle m t -> t
table _ = rep