yesod-session-persist-0.0.0.0: internal/Yesod/Session/Storage/Save.hs
module Yesod.Session.Storage.Save
( save
) where
import Internal.Prelude
import Control.Monad.Trans.Maybe (MaybeT (MaybeT), runMaybeT)
import Data.Map.Strict qualified as Map
import Session.Key
import Session.Timing.Options
import Session.Timing.Time
import Time
import Yesod.Core (SessionMap)
import Yesod.Session.Options
import Yesod.Session.SessionType
import Yesod.Session.Storage.Operation
-- | Save a session to the database
--
-- Return value of 'Nothing' indicates that no changes were made.
-- 'Just' is returned when a session was saved, either by an insert
-- or a replace operation.
save
:: Monad tx
=> Options tx m
-> (forall a. StorageOperation a -> tx a)
-> SessionKeyManager tx
-> UTCTime
-- ^ The current time
-> SessionMap
-- ^ The new session data to be saved
-> Maybe Session
-- ^ What's in the database
-> tx (Maybe Session)
save options storage sessionKeyManager now newInfo oldSessionMaybe =
asumM
[ runMaybeT $ do
guardMaybeT $ isNothing oldSessionMaybe
guardMaybeT $ Map.null newInfo
pure Nothing
, runMaybeT $ do
-- If the data is the same and the old access time is within
-- the timeout resolution, just return the old session without
-- doing anything else.
res <- assertJust options.timing.resolution
old <- assertJust oldSessionMaybe
guardMaybeT $ old.map == newInfo
guardMaybeT $ diffUTCTime now old.time.accessed < res
pure Nothing
, runMaybeT $ do
oldSession <- assertJust oldSessionMaybe
let newSession =
Session
{ key = oldSession.key
, map = newInfo
, time = Time {created = oldSession.time.created, accessed = now}
}
lift $ storage $ ReplaceSession newSession
pure $ Just newSession
]
`orElseM` do
sessionKey <- sessionKeyManager.new
let newSession =
Session
{ key = sessionKey
, map = newInfo
, time = Time {created = now, accessed = now}
}
storage $ InsertSession newSession
pure $ Just newSession
orElseM :: Monad m => m (Maybe a) -> m a -> m a
a `orElseM` b = a >>= maybe b pure
asumM :: Monad m => [m (Maybe a)] -> m (Maybe a)
asumM = \case [] -> pure Nothing; x : xs -> x >>= maybe (asumM xs) (pure . Just)
guardMaybeT :: Monad m => Bool -> MaybeT m ()
guardMaybeT = \case True -> pure (); False -> MaybeT (pure Nothing)
assertJust :: Monad m => Maybe a -> MaybeT m a
assertJust = MaybeT . pure