packages feed

servant-effectful-1.0.0: src/Effectful/Servant/Server.hs

module Effectful.Servant.Server
    ( -- * Run a wai application from an API
      serve
    , serveWithContext
    , serveWithContextT
    , ServerContext

      -- * Handlers for all standard combinators
    , HasServer (..)
    , Server
    , EmptyServer
    , emptyServer
    , Handler

      -- * Context
    , Context (..)
    , HasContextEntry (getContextEntry)
    , type (.++)
    , (.++)

      -- ** NamedContext
    , NamedContext (..)
    , descendIntoNamedContext

      -- * Basic Authentication
    , BasicAuthCheck (BasicAuthCheck, unBasicAuthCheck)
    , BasicAuthResult (..)

      -- * Default error type
    , ServerError (..)

      -- ** 3XX
    , err300
    , err301
    , err302
    , err303
    , err304
    , err305
    , err307

      -- ** 4XX
    , err400
    , err401
    , err402
    , err403
    , err404
    , err405
    , err406
    , err407
    , err409
    , err410
    , err411
    , err412
    , err413
    , err414
    , err415
    , err416
    , err417
    , err418
    , err422
    , err429

      -- ** 5XX
    , err500
    , err501
    , err502
    , err503
    , err504
    , err505
    )
where

import Control.Monad.Trans.Except (ExceptT (ExceptT))
import Data.Kind (Type)
import Data.Proxy (Proxy)
import Effectful
import Effectful.Error.Static (Error, runErrorNoCallStack)
import Effectful.Wai (Application, liftApplication)
import Servant.Server hiding
    ( Application
    , Handler
    , Server
    , respond
    , serve
    , serveWithContext
    , serveWithContextT
    )
import Servant.Server qualified as Servant
import Prelude

-- | Lifted 'Servant.Handler'.
type Handler (es :: [Effect]) = Eff (Error ServerError ': es)

-- | Lifted 'Servant.Server'.
type Server (api :: Type) (es :: [Effect]) = ServerT api (Handler es)

-- | Lifted 'Servant.serve'.
serve
    :: ( HasServer api ('[] :: [Type])
       , IOE :> es
       )
    => Proxy api
    -> Server api es
    -> Application es
serve p = serveWithContext p EmptyContext

-- | Lifted 'Servant.serveWithContext'.
serveWithContext
    :: ( HasServer api context
       , ServerContext context
       , IOE :> es
       )
    => Proxy api
    -> Context context
    -> Server api es
    -> Application es
serveWithContext p context = serveWithContextT p context id

-- | Lifted 'Servant.serveWithContextT'.
serveWithContextT
    :: ( HasServer api context
       , ServerContext context
       , IOE :> es
       )
    => Proxy api
    -> Context context
    -> (forall a. m a -> Handler es a)
    -> ServerT api m
    -> Application es
serveWithContextT p context toHandler server req respond =
    withEffToIO SeqUnlift \unlift ->
        unlift $
            liftApplication
                ( Servant.serveWithContextT
                    p
                    context
                    ( Servant.Handler
                        . ExceptT
                        . unlift
                        . runErrorNoCallStack
                        . toHandler
                    )
                    server
                )
                req
                respond