@@ -1,23 +1,22 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
22{-# LANGUAGE ConstraintKinds #-}
33{-# LANGUAGE DataKinds #-}
44{-# LANGUAGE DeriveDataTypeable #-}
55{-# LANGUAGE FlexibleContexts #-}
66{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE InstanceSigs #-}
78{-# LANGUAGE MultiParamTypeClasses #-}
89{-# LANGUAGE OverloadedStrings #-}
910{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE QuantifiedConstraints #-}
1012{-# LANGUAGE RankNTypes #-}
1113{-# LANGUAGE ScopedTypeVariables #-}
1214{-# LANGUAGE TupleSections #-}
15+{-# LANGUAGE TypeApplications #-}
1316{-# LANGUAGE TypeFamilies #-}
1417{-# LANGUAGE TypeOperators #-}
1518{-# LANGUAGE UndecidableInstances #-}
1619
17−#if MIN_VERSION_base(4,9,0) && __GLASGOW_HASKELL__ >= 802
18−#define HAS_TYPE_ERROR
19−#endif
20−
2120module Servant.Server.Internal
2221 ( module Servant.Server.Internal
2322 , module Servant.Server.Internal.BasicAuth
@@ -42,6 +41,7 @@ import qualified Data.ByteString as B
4241import qualified Data.ByteString.Builder as BB
4342import qualified Data.ByteString.Char8 as BC8
4443import qualified Data.ByteString.Lazy as BL
44+import Data.Constraint (Dict(..))
4545import Data.Either
4646 (partitionEithers)
4747import Data.Maybe
@@ -54,6 +54,7 @@ import Data.Tagged
5454 (Tagged (..), retag, untag)
5555import qualified Data.Text as T
5656import Data.Typeable
57+import GHC.Generics
5758import GHC.TypeLits
5859 (KnownNat, KnownSymbol, natVal, symbolVal)
5960import qualified Network.HTTP.Media as NHM
@@ -75,7 +76,8 @@ import Servant.API
7576 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
7677 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
7778 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
78− WithNamedContext)
79+ WithNamedContext, NamedRoutes)
80+import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
7981import Servant.API.ContentTypes
8082 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
8183 AllMime, MimeRender (..), MimeUnrender (..), NoContent,
@@ -101,12 +103,10 @@ import Servant.Server.Internal.RouteResult
101103import Servant.Server.Internal.RoutingApplication
102104import Servant.Server.Internal.ServerError
103105
104−#ifdef HAS_TYPE_ERROR
105106import GHC.TypeLits
106107 (ErrorMessage (..), TypeError)
107108import Servant.API.TypeLevel
108109 (AtLeastOneFragment, FragmentUnique)
109−#endif
110110
111111class HasServer api context where
112112 type ServerT api (m :: * -> *) :: *
@@ -784,7 +784,7 @@ instance ( KnownSymbol realm
784784-- * helpers
785785
786786ct_wildcard :: B.ByteString
787−ct_wildcard = "*" <> "/" <> "*" -- Because CPP
787+ct_wildcard = "*" <> "/" <> "*"
788788
789789getAcceptHeader :: Request -> AcceptHeader
790790getAcceptHeader = AcceptHeader . fromMaybe ct_wildcard . lookup hAccept . requestHeaders
@@ -815,7 +815,6 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
815815-- TypeError helpers
816816-------------------------------------------------------------------------------
817817
818−#ifdef HAS_TYPE_ERROR
819818-- | This instance catches mistakes when there are non-saturated
820819-- type applications on LHS of ':>'.
821820--
@@ -878,7 +877,6 @@ type HasServerArrowTypeError a b =
878877 ':$$: 'ShowType a
879878 ':$$: 'Text "and"
880879 ':$$: 'ShowType b
881−#endif
882880
883881-- | Ignore @'Fragment'@ in server handlers.
884882-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
@@ -891,11 +889,7 @@ type HasServerArrowTypeError a b =
891889-- > server = getBooks
892890-- > where getBooks :: Handler [Book]
893891-- > getBooks = ...return all books...
894−#ifdef HAS_TYPE_ERROR
895892instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context)
896−#else
897−instance (HasServer api context)
898−#endif
899893 => HasServer (Fragment a1 :> api) context where
900894 type ServerT (Fragment a1 :> api) m = ServerT api m
901895
@@ -905,3 +899,72 @@ instance (HasServer api context)
905899
906900-- $setup
907901-- >>> import Servant
902+
903+-- | A type that specifies that an API record contains a server implementation.
904+data AsServerT (m :: * -> *)
905+instance GenericMode (AsServerT m) where
906+ type AsServerT m :- api = ServerT api m
907+
908+type AsServer = AsServerT Handler
909+
910+
911+-- | Set of constraints required to convert to / from vanilla server types.
912+type GServerConstraints api m =
913+ ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
914+ , GServantProduct (Rep (api (AsServerT m)))
915+ )
916+
917+-- | This class is a necessary evil: in the implementation of 'HasServer' for
918+-- @'NamedRoutes' api@, we essentially need the quantified constraint @forall
919+-- m. 'GServerConstraints' m@ to hold.
920+--
921+-- We cannot require do that directly as the definition of 'GServerConstraints'
922+-- contains type family applications ('Rep' and 'ServerT'). The trick is to hide
923+-- those type family applications behind a typeclass providing evidence for
924+-- @'GServerConstraints' api m@ in the form of a dictionary, and require that
925+-- @forall m. 'GServer' api m@ instead.
926+--
927+-- Users shouldn't have to worry about this class, as the only possible instance
928+-- is provided in this module for all record APIs.
929+
930+class GServer (api :: * -> *) (m :: * -> *) where
931+ proof :: Dict (GServerConstraints api m)
932+
933+instance
934+ ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
935+ , GServantProduct (Rep (api (AsServerT m)))
936+ ) => GServer api m where
937+ proof = Dict
938+
939+instance
940+ ( HasServer (ToServantApi api) context
941+ , forall m. Generic (api (AsServerT m))
942+ , forall m. GServer api m
943+ ) => HasServer (NamedRoutes api) context where
944+
945+ type ServerT (NamedRoutes api) m = api (AsServerT m)
946+
947+ route
948+ :: Proxy (NamedRoutes api)
949+ -> Context context
950+ -> Delayed env (api (AsServerT Handler))
951+ -> Router env
952+ route _ ctx delayed =
953+ case proof @api @Handler of
954+ Dict -> route (Proxy @(ToServantApi api)) ctx (toServant <$> delayed)
955+
956+ hoistServerWithContext
957+ :: forall m n. Proxy (NamedRoutes api)
958+ -> Proxy context
959+ -> (forall x. m x -> n x)
960+ -> api (AsServerT m)
961+ -> api (AsServerT n)
962+ hoistServerWithContext _ pctx nat server =
963+ case (proof @api @m, proof @api @n) of
964+ (Dict, Dict) ->
965+ fromServant servantSrvN
966+ where
967+ servantSrvM :: ServerT (ToServantApi api) m =
968+ toServant server
969+ servantSrvN :: ServerT (ToServantApi api) n =
970+ hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM