servant/servant-server/src/Servant/Server/Internal/DelayedIO.hs

72 lines
2.7 KiB
Haskell
Raw Normal View History

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
-- '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