@@ -1,15 +1,18 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE AllowAmbiguousTypes #-}
23{-# LANGUAGE ConstraintKinds #-}
34{-# LANGUAGE DataKinds #-}
45{-# LANGUAGE DeriveDataTypeable #-}
56{-# LANGUAGE FlexibleContexts #-}
67{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE InstanceSigs #-}
79{-# LANGUAGE MultiParamTypeClasses #-}
810{-# LANGUAGE OverloadedStrings #-}
911{-# LANGUAGE PolyKinds #-}
1012{-# LANGUAGE RankNTypes #-}
1113{-# LANGUAGE ScopedTypeVariables #-}
1214{-# LANGUAGE TupleSections #-}
15+{-# LANGUAGE TypeApplications #-}
1316{-# LANGUAGE TypeFamilies #-}
1417{-# LANGUAGE TypeOperators #-}
1518{-# LANGUAGE UndecidableInstances #-}
@@ -18,6 +21,10 @@
1821#define HAS_TYPE_ERROR
1922#endif
2023
24+#if __GLASGOW_HASKELL__ >= 806
25+{-# LANGUAGE QuantifiedConstraints #-}
26+#endif
27+
2128module Servant.Server.Internal
2229 ( module Servant.Server.Internal
2330 , module Servant.Server.Internal.BasicAuth
@@ -42,6 +49,7 @@ import qualified Data.ByteString as B
4249import qualified Data.ByteString.Builder as BB
4350import qualified Data.ByteString.Char8 as BC8
4451import qualified Data.ByteString.Lazy as BL
52+import Data.Constraint (Dict(..))
4553import Data.Either
4654 (partitionEithers)
4755import Data.Maybe
@@ -54,6 +62,7 @@ import Data.Tagged
5462 (Tagged (..), retag, untag)
5563import qualified Data.Text as T
5664import Data.Typeable
65+import GHC.Generics
5766import GHC.TypeLits
5867 (KnownNat, KnownSymbol, natVal, symbolVal)
5968import qualified Network.HTTP.Media as NHM
@@ -75,7 +84,8 @@ import Servant.API
7584 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
7685 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
7786 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
78− WithNamedContext)
87+ WithNamedContext, NamedRoutes)
88+import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
7989import Servant.API.ContentTypes
8090 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
8191 AllMime, MimeRender (..), MimeUnrender (..), NoContent,
@@ -905,3 +915,76 @@ instance (HasServer api context)
905915
906916-- $setup
907917-- >>> import Servant
918+
919+-- | A type that specifies that an API record contains a server implementation.
920+data AsServerT (m :: * -> *)
921+instance GenericMode (AsServerT m) where
922+ type AsServerT m :- api = ServerT api m
923+
924+type AsServer = AsServerT Handler
925+
926+
927+#if __GLASGOW_HASKELL__ >= 806
928+
929+-- | Set of constraints required to convert to / from vanilla server types.
930+type GServerConstraints api m =
931+ ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
932+ , GServantProduct (Rep (api (AsServerT m)))
933+ )
934+
935+-- | This class is a necessary evil: in the implementation of 'HasServer' for
936+-- @'NamedRoutes' api@, we essentially need the quantified constraint @forall
937+-- m. 'GServerConstraints' m@ to hold.
938+--
939+-- We cannot require do that directly as the definition of 'GServerConstraints'
940+-- contains type family applications ('Rep' and 'ServerT'). The trick is to hide
941+-- those type family applications behind a typeclass providing evidence for
942+-- @'GServerConstraints' api m@ in the form of a dictionary, and require that
943+-- @forall m. 'GServer' api m@ instead.
944+--
945+-- Users shouldn't have to worry about this class, as the only possible instance
946+-- is provided in this module for all record APIs.
947+
948+class GServer (api :: * -> *) (m :: * -> *) where
949+ proof :: Dict (GServerConstraints api m)
950+
951+instance
952+ ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
953+ , GServantProduct (Rep (api (AsServerT m)))
954+ ) => GServer api m where
955+ proof = Dict
956+
957+instance
958+ ( HasServer (ToServantApi api) context
959+ , forall m. Generic (api (AsServerT m))
960+ , forall m. GServer api m
961+ ) => HasServer (NamedRoutes api) context where
962+
963+ type ServerT (NamedRoutes api) m = api (AsServerT m)
964+
965+ route
966+ :: Proxy (NamedRoutes api)
967+ -> Context context
968+ -> Delayed env (api (AsServerT Handler))
969+ -> Router env
970+ route _ ctx delayed =
971+ case proof @api @Handler of
972+ Dict -> route (Proxy @(ToServantApi api)) ctx (toServant <$> delayed)
973+
974+ hoistServerWithContext
975+ :: forall m n. Proxy (NamedRoutes api)
976+ -> Proxy context
977+ -> (forall x. m x -> n x)
978+ -> api (AsServerT m)
979+ -> api (AsServerT n)
980+ hoistServerWithContext _ pctx nat server =
981+ case (proof @api @m, proof @api @n) of
982+ (Dict, Dict) ->
983+ fromServant servantSrvN
984+ where
985+ servantSrvM :: ServerT (ToServantApi api) m =
986+ toServant server
987+ servantSrvN :: ServerT (ToServantApi api) n =
988+ hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM
989+
990+#endif