module Database.Persist.Class.PersistUnique
( PersistUnique (..)
, getByValue
, insertBy
, replaceUnique
, checkUnique
, onlyUnique
) where
import Database.Persist.Types
import qualified Prelude
import Prelude hiding ((++))
import Control.Exception (throwIO)
import Control.Monad (liftM, when)
import Control.Monad.IO.Class (liftIO)
import Data.List ((\\))
import Control.Monad.Trans.Reader ( ReaderT )
import Control.Monad.IO.Class (MonadIO)
import Database.Persist.Class.PersistStore
import Database.Persist.Class.PersistEntity
import Data.Monoid (mappend)
import Data.Text (unpack, Text)
class PersistStore backend => PersistUnique backend where
getBy :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val) => Unique val -> ReaderT backend m (Maybe (Entity val))
deleteBy :: (MonadIO m, PersistEntityBackend val ~ backend, PersistEntity val) => Unique val -> ReaderT backend m ()
insertUnique :: (MonadIO m, PersistEntityBackend val ~ backend, PersistEntity val) => val -> ReaderT backend m (Maybe (Key val))
insertUnique datum = do
conflict <- checkUnique datum
case conflict of
Nothing -> Just `liftM` insert datum
Just _ -> return Nothing
upsert :: (MonadIO m, PersistEntityBackend val ~ backend, PersistEntity val)
=> val
-> [Update val]
-> ReaderT backend m (Entity val)
upsert record updates = do
uniqueKey <- onlyUnique record
mExists <- getBy uniqueKey
k <- case mExists of
Just (Entity k _) -> do
when (null updates) (replace k record)
return k
Nothing -> insert record
Entity k `liftM` updateGet k updates
insertBy :: (MonadIO m, PersistEntity val, PersistUnique backend, PersistEntityBackend val ~ backend)
=> val -> ReaderT backend m (Either (Entity val) (Key val))
insertBy val = do
res <- getByValue val
case res of
Nothing -> Right `liftM` insert val
Just z -> return $ Left z
onlyUnique :: (MonadIO m, PersistEntity val, PersistUnique backend, PersistEntityBackend val ~ backend)
=> val -> ReaderT backend m (Unique val)
onlyUnique record = case onlyUniqueEither record of
Right u -> return u
Left us -> requireUniques record us >>= liftIO . throwIO . OnlyUniqueException . show . length
onlyUniqueEither :: (PersistEntity val) => val -> Either [Unique val] (Unique val)
onlyUniqueEither record = case persistUniqueKeys record of
(u:[]) -> Right u
us -> Left us
getByValue :: (MonadIO m, PersistEntity record, PersistUnique backend, PersistEntityBackend record ~ backend)
=> record -> ReaderT backend m (Maybe (Entity record))
getByValue record = checkUniques =<< requireUniques record (persistUniqueKeys record)
where
checkUniques [] = return Nothing
checkUniques (x:xs) = do
y <- getBy x
case y of
Nothing -> checkUniques xs
Just z -> return $ Just z
requireUniques :: (MonadIO m, PersistEntity record) => record -> [Unique record] -> m [Unique record]
requireUniques record [] = liftIO $ throwIO $ userError errorMsg
where
errorMsg = "getByValue: " `mappend` unpack (recordName record) `mappend` " does not have any Unique"
requireUniques _ xs = return xs
recordName :: (PersistEntity record) => record -> Text
recordName = unHaskellName . entityHaskell . entityDef . Just
replaceUnique :: (MonadIO m, Eq record, Eq (Unique record), PersistEntityBackend record ~ backend, PersistEntity record, PersistUnique backend)
=> Key record -> record -> ReaderT backend m (Maybe (Unique record))
replaceUnique key datumNew = getJust key >>= replaceOriginal
where
uniqueKeysNew = persistUniqueKeys datumNew
replaceOriginal original = do
conflict <- checkUniqueKeys changedKeys
case conflict of
Nothing -> replace key datumNew >> return Nothing
(Just conflictingKey) -> return $ Just conflictingKey
where
changedKeys = uniqueKeysNew \\ uniqueKeysOriginal
uniqueKeysOriginal = persistUniqueKeys original
checkUnique :: (MonadIO m, PersistEntityBackend record ~ backend, PersistEntity record, PersistUnique backend)
=> record -> ReaderT backend m (Maybe (Unique record))
checkUnique = checkUniqueKeys . persistUniqueKeys
checkUniqueKeys :: (MonadIO m, PersistEntity record, PersistUnique backend, PersistEntityBackend record ~ backend)
=> [Unique record] -> ReaderT backend m (Maybe (Unique record))
checkUniqueKeys [] = return Nothing
checkUniqueKeys (x:xs) = do
y <- getBy x
case y of
Nothing -> checkUniqueKeys xs
Just _ -> return (Just x)