intelli-monad-0.1.0.0: src/IntelliMonad/Persist.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module IntelliMonad.Persist where
import Control.Monad.IO.Class
import Data.List (maximumBy)
import qualified Data.Set as S
import Data.Text (Text)
import Database.Persist
import Database.Persist.Sqlite
import IntelliMonad.Types
data StatelessConf = StatelessConf
class PersistentBackend p where
type Conn p
config :: p
setup :: (MonadIO m, MonadFail m) => p -> m (Maybe (Conn p))
initialize :: (MonadIO m, MonadFail m) => Conn p -> Context -> m ()
load :: (MonadIO m, MonadFail m) => Conn p -> SessionName -> m (Maybe Context)
loadByKey :: (MonadIO m, MonadFail m) => Conn p -> (Key Context) -> m (Maybe Context)
save :: (MonadIO m, MonadFail m) => Conn p -> Context -> m (Maybe (Key Context))
saveContents :: (MonadIO m, MonadFail m) => Conn p -> [Content] -> m ()
listSessions :: (MonadIO m, MonadFail m) => Conn p -> m [Text]
deleteSession :: (MonadIO m, MonadFail m) => Conn p -> SessionName -> m ()
instance PersistentBackend SqliteConf where
type Conn SqliteConf = ConnectionPool
config =
SqliteConf
{ sqlDatabase = "intelli-monad.sqlite3",
sqlPoolSize = 5
}
setup p = do
conn <- liftIO $ createPoolConfig p
liftIO $ runPool p (runMigration migrateAll) conn
return $ Just conn
initialize conn context = do
_ <- liftIO $ runPool (config @SqliteConf) (insert context) conn
return ()
load conn sessionName = do
(a :: [Entity Context]) <- liftIO $ runPool (config @SqliteConf) (selectList [ContextSessionName ==. sessionName] []) conn
if length a == 0
then return Nothing
else return $ Just $ maximumBy (\a0 a1 -> compare (contextCreated a1) (contextCreated a0)) $ map (\(Entity _ v) -> v) a
loadByKey conn key = do
(a :: [Entity Context]) <- liftIO $ runPool (config @SqliteConf) (selectList [ContextId ==. key] []) conn
if length a == 0
then return Nothing
else return $ Just $ maximumBy (\a0 a1 -> compare (contextCreated a1) (contextCreated a0)) $ map (\(Entity _ v) -> v) a
save conn context = do
liftIO $ runPool (config @SqliteConf) (Just <$> insert context) conn
saveContents conn contents = do
liftIO $ runPool (config @SqliteConf) (putMany contents) conn
listSessions conn = do
(a :: [Entity Context]) <- liftIO $ runPool (config @SqliteConf) (selectList [] []) conn
return $ S.toList $ S.fromList $ map (\(Entity _ v) -> contextSessionName v) a
deleteSession conn sessionName = do
liftIO $ runPool (config @SqliteConf) (deleteWhere [ContextSessionName ==. sessionName]) conn
instance PersistentBackend StatelessConf where
type Conn StatelessConf = ()
config = StatelessConf
setup _ = return $ Just ()
initialize _ _ = return ()
load _ _ = return Nothing
loadByKey _ _ = return Nothing
save _ _ = return Nothing
saveContents _ _ = return ()
listSessions _ = return []
deleteSession _ _ = return ()
withDB :: forall p m a. (MonadIO m, MonadFail m, PersistentBackend p) => (Conn p -> m a) -> m a
withDB func =
setup (config @p) >>= \case
Nothing -> fail "Can not open a database."
Just (conn :: Conn p) -> func conn