2019-02-27 12:04:33 +01:00
|
|
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
module Servant.Server.Internal.DelayedIO where
|
|
|
|
|
|
|
|
import Control.Monad.Base
|
|
|
|
(MonadBase (..))
|
|
|
|
import Control.Monad.Catch
|
|
|
|
(MonadThrow (..))
|
|
|
|
import Control.Monad.Reader
|
|
|
|
(MonadReader (..), ReaderT (..), runReaderT)
|
|
|
|
import Control.Monad.Trans
|
|
|
|
(MonadIO (..), MonadTrans (..))
|
|
|
|
import Control.Monad.Trans.Control
|
2019-12-03 21:37:32 +01:00
|
|
|
(MonadBaseControl (..))
|
2019-02-27 12:04:33 +01:00
|
|
|
import Control.Monad.Trans.Resource
|
|
|
|
(MonadResource (..), ResourceT, runInternalState,
|
2019-12-03 21:37:32 +01:00
|
|
|
transResourceT, withInternalState)
|
2019-02-27 12:04:33 +01:00
|
|
|
import Network.Wai
|
2019-12-03 21:37:32 +01:00
|
|
|
(Request)
|
2019-02-27 12:04:33 +01:00
|
|
|
|
|
|
|
import Servant.Server.Internal.RouteResult
|
|
|
|
import Servant.Server.Internal.ServerError
|
|
|
|
|
|
|
|
-- | Computations used in a 'Delayed' can depend on the
|
|
|
|
-- incoming 'Request', may perform 'IO', and result in a
|
2020-06-06 06:43:51 +02:00
|
|
|
-- 'RouteResult', meaning they can either succeed, fail
|
2019-02-27 12:04:33 +01:00
|
|
|
-- (with the possibility to recover), or fail fatally.
|
|
|
|
--
|
|
|
|
newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO)) a }
|
|
|
|
deriving
|
|
|
|
( Functor, Applicative, Monad
|
|
|
|
, MonadIO, MonadReader Request
|
|
|
|
, MonadThrow
|
|
|
|
, MonadResource
|
|
|
|
)
|
|
|
|
|
|
|
|
instance MonadBase IO DelayedIO where
|
|
|
|
liftBase = liftIO
|
|
|
|
|
|
|
|
liftRouteResult :: RouteResult a -> DelayedIO a
|
|
|
|
liftRouteResult x = DelayedIO $ lift . lift $ RouteResultT . return $ x
|
|
|
|
|
|
|
|
instance MonadBaseControl IO DelayedIO where
|
|
|
|
-- type StM DelayedIO a = StM (ReaderT Request (ResourceT (RouteResultT IO))) a
|
|
|
|
-- liftBaseWith f = DelayedIO $ liftBaseWith $ \g -> f (g . runDelayedIO')
|
|
|
|
-- restoreM = DelayedIO . restoreM
|
|
|
|
|
|
|
|
type StM DelayedIO a = RouteResult a
|
|
|
|
liftBaseWith f = DelayedIO $ ReaderT $ \req -> withInternalState $ \s ->
|
|
|
|
liftBaseWith $ \runInBase -> f $ \x ->
|
|
|
|
runInBase (runInternalState (runReaderT (runDelayedIO' x) req) s)
|
|
|
|
restoreM = DelayedIO . lift . withInternalState . const . restoreM
|
|
|
|
|
|
|
|
|
|
|
|
runDelayedIO :: DelayedIO a -> Request -> ResourceT IO (RouteResult a)
|
|
|
|
runDelayedIO m req = transResourceT runRouteResultT $ runReaderT (runDelayedIO' m) req
|
|
|
|
|
|
|
|
-- | Fail with the option to recover.
|
|
|
|
delayedFail :: ServerError -> DelayedIO a
|
|
|
|
delayedFail err = liftRouteResult $ Fail err
|
|
|
|
|
|
|
|
-- | Fail fatally, i.e., without any option to recover.
|
|
|
|
delayedFailFatal :: ServerError -> DelayedIO a
|
|
|
|
delayedFailFatal err = liftRouteResult $ FailFatal err
|
|
|
|
|
|
|
|
-- | Gain access to the incoming request.
|
|
|
|
withRequest :: (Request -> DelayedIO a) -> DelayedIO a
|
|
|
|
withRequest f = do
|
|
|
|
req <- ask
|
|
|
|
f req
|