{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE RankNTypes             #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE TypeOperators          #-}

-- | @since 0.14.1
module Servant.Server.Generic (
    AsServerT,
    AsServer,
    genericServe,
    genericServeT,
    genericServeTWithContext,
    genericServer,
    genericServerT
  ) where

import           Data.Kind
                 (Type)
import           Data.Proxy
                 (Proxy (..))

import           Servant.Server
import           Servant.API.Generic
import           Servant.Server.Internal

-- | Transform a record of routes into a WAI 'Application'.
genericServe
    :: forall routes.
       ( HasServer (ToServantApi routes) '[]
       , GenericServant routes AsServer
       , Server (ToServantApi routes) ~ ToServant routes AsServer
       )
    => routes AsServer -> Application
genericServe :: forall (routes :: Type -> Type).
(HasServer (ToServantApi routes) '[],
 GenericServant routes AsServer,
 Server (ToServantApi routes) ~ ToServant routes AsServer) =>
routes AsServer -> Application
genericServe = Proxy (ToServantApi routes)
-> Server (ToServantApi routes) -> Application
forall {k} (api :: k).
HasServer api '[] =>
Proxy api -> Server api -> Application
serve (Proxy (ToServantApi routes)
forall {k} (t :: k). Proxy t
Proxy :: Proxy (ToServantApi routes))  (Server (ToServantApi routes) -> Application)
-> (routes AsServer -> Server (ToServantApi routes))
-> routes AsServer
-> Application
forall b c a. (b -> c) -> (a -> b) -> a -> c
. routes AsServer -> ToServant routes AsServer
routes AsServer -> Server (ToServantApi routes)
forall (routes :: Type -> Type).
GenericServant routes AsServer =>
routes AsServer -> ToServant routes AsServer
genericServer

-- | Transform a record of routes with custom monad into a WAI 'Application',
--   by providing a transformation to bring each handler back in the 'Handler'
--   monad.
genericServeT
  :: forall (routes :: Type -> Type) (m :: Type -> Type).
     ( GenericServant routes (AsServerT m)
     , GenericServant routes AsApi
     , HasServer (ToServantApi routes) '[]
     , ServerT (ToServantApi routes) m ~ ToServant routes (AsServerT m)
     )
  => (forall a. m a -> Handler a) -- ^ 'hoistServer' argument to come back to 'Handler'
  -> routes (AsServerT m)         -- ^ your record full of request handlers
  -> Application
genericServeT :: forall (routes :: Type -> Type) (m :: Type -> Type).
(GenericServant routes (AsServerT m), GenericServant routes AsApi,
 HasServer (ToServantApi routes) '[],
 ServerT (ToServantApi routes) m
 ~ ToServant routes (AsServerT m)) =>
(forall a. m a -> Handler a) -> routes (AsServerT m) -> Application
genericServeT forall a. m a -> Handler a
f routes (AsServerT m)
server = Proxy (ToServantApi routes)
-> Server (ToServantApi routes) -> Application
forall {k} (api :: k).
HasServer api '[] =>
Proxy api -> Server api -> Application
serve Proxy (ToServantApi routes)
p (Server (ToServantApi routes) -> Application)
-> Server (ToServantApi routes) -> Application
forall a b. (a -> b) -> a -> b
$ Proxy (ToServantApi routes)
-> (forall a. m a -> Handler a)
-> ServerT (ToServantApi routes) m
-> Server (ToServantApi routes)
forall {k} (api :: k) (m :: Type -> Type) (n :: Type -> Type).
HasServer api '[] =>
Proxy api
-> (forall x. m x -> n x) -> ServerT api m -> ServerT api n
hoistServer Proxy (ToServantApi routes)
p m x -> Handler x
forall a. m a -> Handler a
f (routes (AsServerT m) -> ToServant routes (AsServerT m)
forall (routes :: Type -> Type) (m :: Type -> Type).
GenericServant routes (AsServerT m) =>
routes (AsServerT m) -> ToServant routes (AsServerT m)
genericServerT routes (AsServerT m)
server)
  where
    p :: Proxy (ToServantApi routes)
p = Proxy routes -> Proxy (ToServantApi routes)
forall (routes :: Type -> Type).
GenericServant routes AsApi =>
Proxy routes -> Proxy (ToServantApi routes)
genericApi (Proxy routes
forall {k} (t :: k). Proxy t
Proxy :: Proxy routes)

-- | Transform a record of routes with custom monad into a WAI 'Application',
--   while using the given 'Context' to serve the application (contexts are typically
--   used by auth-related combinators in servant, e.g to hold auth checks) and the given
--   transformation to map all the handlers back to the 'Handler' monad.
genericServeTWithContext
  :: forall (routes :: Type -> Type) (m :: Type -> Type) (ctx :: [Type]).
     ( GenericServant routes (AsServerT m)
     , GenericServant routes AsApi
     , HasServer (ToServantApi routes) ctx
     , HasContextEntry (ctx .++ DefaultErrorFormatters) ErrorFormatters
     , ServerT (ToServantApi routes) m ~ ToServant routes (AsServerT m)
     )
  => (forall a. m a -> Handler a) -- ^ 'hoistServer' argument to come back to 'Handler'
  -> routes (AsServerT m)         -- ^ your record full of request handlers
  -> Context ctx                  -- ^ the 'Context' to serve the application with
  -> Application
genericServeTWithContext :: forall (routes :: Type -> Type) (m :: Type -> Type)
       (ctx :: [Type]).
(GenericServant routes (AsServerT m), GenericServant routes AsApi,
 HasServer (ToServantApi routes) ctx,
 HasContextEntry (ctx .++ DefaultErrorFormatters) ErrorFormatters,
 ServerT (ToServantApi routes) m
 ~ ToServant routes (AsServerT m)) =>
(forall a. m a -> Handler a)
-> routes (AsServerT m) -> Context ctx -> Application
genericServeTWithContext forall a. m a -> Handler a
f routes (AsServerT m)
server Context ctx
ctx =
  Proxy (ToServantApi routes)
-> Context ctx -> Server (ToServantApi routes) -> Application
forall {k} (api :: k) (context :: [Type]).
(HasServer api context, ServerContext context) =>
Proxy api -> Context context -> Server api -> Application
serveWithContext Proxy (ToServantApi routes)
p Context ctx
ctx (Server (ToServantApi routes) -> Application)
-> Server (ToServantApi routes) -> Application
forall a b. (a -> b) -> a -> b
$
  Proxy (ToServantApi routes)
-> Proxy ctx
-> (forall a. m a -> Handler a)
-> ServerT (ToServantApi routes) m
-> Server (ToServantApi routes)
forall {k} (api :: k) (context :: [Type]) (m :: Type -> Type)
       (n :: Type -> Type).
HasServer api context =>
Proxy api
-> Proxy context
-> (forall x. m x -> n x)
-> ServerT api m
-> ServerT api n
forall (m :: Type -> Type) (n :: Type -> Type).
Proxy (ToServantApi routes)
-> Proxy ctx
-> (forall x. m x -> n x)
-> ServerT (ToServantApi routes) m
-> ServerT (ToServantApi routes) n
hoistServerWithContext Proxy (ToServantApi routes)
p Proxy ctx
pctx m x -> Handler x
forall a. m a -> Handler a
f (routes (AsServerT m) -> ToServant routes (AsServerT m)
forall (routes :: Type -> Type) (m :: Type -> Type).
GenericServant routes (AsServerT m) =>
routes (AsServerT m) -> ToServant routes (AsServerT m)
genericServerT routes (AsServerT m)
server)
  where
    p :: Proxy (ToServantApi routes)
p = Proxy routes -> Proxy (ToServantApi routes)
forall (routes :: Type -> Type).
GenericServant routes AsApi =>
Proxy routes -> Proxy (ToServantApi routes)
genericApi (Proxy routes
forall {k} (t :: k). Proxy t
Proxy :: Proxy routes)
    pctx :: Proxy ctx
pctx = Proxy ctx
forall {k} (t :: k). Proxy t
Proxy :: Proxy ctx

-- | Transform a record of endpoints into a 'Server'.
genericServer
    :: GenericServant routes AsServer
    => routes AsServer
    -> ToServant routes AsServer
genericServer :: forall (routes :: Type -> Type).
GenericServant routes AsServer =>
routes AsServer -> ToServant routes AsServer
genericServer = routes AsServer -> ToServant routes AsServer
forall {k} (routes :: k -> Type) (mode :: k).
GenericServant routes mode =>
routes mode -> ToServant routes mode
toServant

-- | Transform a record of endpoints into a @'ServerT' m@.
--
--  You can see an example usage of this function
--  <https://docs.servant.dev/en/stable/cookbook/generic/Generic.html#using-generics-together-with-a-custom-monad in the Servant Cookbook>.
genericServerT
    :: GenericServant routes (AsServerT m)
    => routes (AsServerT m)
    -> ToServant routes (AsServerT m)
genericServerT :: forall (routes :: Type -> Type) (m :: Type -> Type).
GenericServant routes (AsServerT m) =>
routes (AsServerT m) -> ToServant routes (AsServerT m)
genericServerT = routes (AsServerT m) -> ToServant routes (AsServerT m)
forall {k} (routes :: k -> Type) (mode :: k).
GenericServant routes mode =>
routes mode -> ToServant routes mode
toServant