{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ConstraintKinds #-}
module Database.Persist.Class.PersistStore
( HasPersistBackend (..)
, IsPersistBackend (..)
, PersistRecordBackend
, liftPersist
, PersistCore (..)
, PersistStoreRead (..)
, PersistStoreWrite (..)
, getEntity
, getJust
, getJustEntity
, belongsTo
, belongsToJust
, insertEntity
, insertRecord
, ToBackendKey(..)
, BackendCompatible(..)
) where
import qualified Data.Text as T
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Exception (throwIO)
import Control.Monad.Trans.Reader (ReaderT)
import Control.Monad.Reader (MonadReader (ask), runReaderT)
import Database.Persist.Class.PersistEntity
import Database.Persist.Class.PersistField
import Database.Persist.Types
import qualified Data.Aeson as A
import qualified Data.Map as Map
import Data.Map (Map)
import qualified Data.Maybe as Maybe
class HasPersistBackend backend where
type BaseBackend backend
persistBackend :: backend -> BaseBackend backend
class (HasPersistBackend backend) => IsPersistBackend backend where
mkPersistBackend :: BaseBackend backend -> backend
class BackendCompatible sup sub where
projectBackend :: sub -> sup
type PersistRecordBackend record backend = (PersistEntity record, PersistEntityBackend record ~ BaseBackend backend)
liftPersist
:: (MonadIO m, MonadReader backend m, HasPersistBackend backend)
=> ReaderT (BaseBackend backend) IO b -> m b
liftPersist f = do
env <- ask
liftIO $ runReaderT f (persistBackend env)
class ( PersistEntity record
, PersistEntityBackend record ~ backend
, PersistCore backend
) => ToBackendKey backend record where
toBackendKey :: Key record -> BackendKey backend
fromBackendKey :: BackendKey backend -> Key record
class PersistCore backend where
data BackendKey backend
class
( Show (BackendKey backend), Read (BackendKey backend)
, Eq (BackendKey backend), Ord (BackendKey backend)
, PersistCore backend
, PersistField (BackendKey backend), A.ToJSON (BackendKey backend), A.FromJSON (BackendKey backend)
) => PersistStoreRead backend where
get :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> ReaderT backend m (Maybe record)
getMany
:: (MonadIO m, PersistRecordBackend record backend)
=> [Key record] -> ReaderT backend m (Map (Key record) record)
getMany [] = return Map.empty
getMany ks = do
vs <- mapM get ks
let kvs = zip ks vs
let kvs' = (fmap Maybe.fromJust) `fmap` filter (\(_,v) -> Maybe.isJust v) kvs
return $ Map.fromList kvs'
class
( Show (BackendKey backend), Read (BackendKey backend)
, Eq (BackendKey backend), Ord (BackendKey backend)
, PersistStoreRead backend
, PersistField (BackendKey backend), A.ToJSON (BackendKey backend), A.FromJSON (BackendKey backend)
) => PersistStoreWrite backend where
insert :: (MonadIO m, PersistRecordBackend record backend)
=> record -> ReaderT backend m (Key record)
insert_ :: (MonadIO m, PersistRecordBackend record backend)
=> record -> ReaderT backend m ()
insert_ record = insert record >> return ()
insertMany :: (MonadIO m, PersistRecordBackend record backend)
=> [record] -> ReaderT backend m [Key record]
insertMany = mapM insert
insertMany_ :: (MonadIO m, PersistRecordBackend record backend)
=> [record] -> ReaderT backend m ()
insertMany_ x = insertMany x >> return ()
insertEntityMany :: (MonadIO m, PersistRecordBackend record backend)
=> [Entity record] -> ReaderT backend m ()
insertEntityMany = mapM_ (\(Entity k record) -> insertKey k record)
insertKey :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> record -> ReaderT backend m ()
repsert :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> record -> ReaderT backend m ()
repsertMany
:: (MonadIO m, PersistRecordBackend record backend)
=> [(Key record, record)] -> ReaderT backend m ()
repsertMany = mapM_ (uncurry repsert)
replace :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> record -> ReaderT backend m ()
delete :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> ReaderT backend m ()
update :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> [Update record] -> ReaderT backend m ()
updateGet :: (MonadIO m, PersistRecordBackend record backend)
=> Key record -> [Update record] -> ReaderT backend m record
updateGet key ups = do
update key ups
get key >>= maybe (liftIO $ throwIO $ KeyNotFound $ show key) return
getJust :: ( PersistStoreRead backend
, Show (Key record)
, PersistRecordBackend record backend
, MonadIO m
) => Key record -> ReaderT backend m record
getJust key = get key >>= maybe
(liftIO $ throwIO $ PersistForeignConstraintUnmet $ T.pack $ show key)
return
getJustEntity
:: (PersistEntityBackend record ~ BaseBackend backend
,MonadIO m
,PersistEntity record
,PersistStoreRead backend)
=> Key record -> ReaderT backend m (Entity record)
getJustEntity key = do
record <- getJust key
return $
Entity
{ entityKey = key
, entityVal = record
}
belongsTo ::
( PersistStoreRead backend
, PersistEntity ent1
, PersistRecordBackend ent2 backend
, MonadIO m
) => (ent1 -> Maybe (Key ent2)) -> ent1 -> ReaderT backend m (Maybe ent2)
belongsTo foreignKeyField model = case foreignKeyField model of
Nothing -> return Nothing
Just f -> get f
belongsToJust ::
( PersistStoreRead backend
, PersistEntity ent1
, PersistRecordBackend ent2 backend
, MonadIO m
)
=> (ent1 -> Key ent2) -> ent1 -> ReaderT backend m ent2
belongsToJust getForeignKey model = getJust $ getForeignKey model
insertEntity ::
( PersistStoreWrite backend
, PersistRecordBackend e backend
, MonadIO m
) => e -> ReaderT backend m (Entity e)
insertEntity e = do
eid <- insert e
return $ Entity eid e
getEntity ::
( PersistStoreRead backend
, PersistRecordBackend e backend
, MonadIO m
) => Key e -> ReaderT backend m (Maybe (Entity e))
getEntity key = do
maybeModel <- get key
return $ fmap (key `Entity`) maybeModel
insertRecord
:: (PersistEntityBackend record ~ BaseBackend backend
,PersistEntity record
,MonadIO m
,PersistStoreWrite backend)
=> record -> ReaderT backend m record
insertRecord record = do
insert_ record
return $ record