@@ -1,12 +1,20 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE KindSignatures #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE TypeOperators #-}
9−{-# LANGUAGE UndecidableInstances #-}
1+{-# OPTIONS_GHC -fno-warn-orphans #-}
2+{-# LANGUAGE AllowAmbiguousTypes #-}
3+{-# LANGUAGE ConstraintKinds #-}
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE DefaultSignatures #-}
6+{-# LANGUAGE FlexibleContexts #-}
7+{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE InstanceSigs #-}
9+{-# LANGUAGE KindSignatures #-}
10+{-# LANGUAGE MultiParamTypeClasses #-}
11+{-# LANGUAGE QuantifiedConstraints #-}
12+{-# LANGUAGE RankNTypes #-}
13+{-# LANGUAGE ScopedTypeVariables #-}
14+{-# LANGUAGE TypeApplications #-}
15+{-# LANGUAGE TypeFamilies #-}
16+{-# LANGUAGE TypeOperators #-}
17+{-# LANGUAGE UndecidableInstances #-}
1018-- | @since 0.14.1
1119module Servant.Server.Generic (
1220 AsServerT,
@@ -16,13 +24,20 @@ module Servant.Server.Generic (
1624 genericServeTWithContext,
1725 genericServer,
1826 genericServerT,
27+ -- * Internal machinery
28+ GServerConstraints,
29+ GServer,
30+ -- * Re-exports
31+ NamedRoutes
1932 ) where
2033
34+import Data.Constraint
2135import Data.Proxy
2236 (Proxy (..))
2337
2438import Servant.API.Generic
2539import Servant.Server
40+import Servant.Server.Internal
2641
2742-- | A type that specifies that an API record contains a server implementation.
2843data AsServerT (m :: * -> *)
@@ -97,3 +112,64 @@ genericServerT
97112 => routes (AsServerT m)
98113 -> ToServant routes (AsServerT m)
99114genericServerT = toServant
115+
116+-- | Set of constraints required to convert to / from vanilla server types.
117+type GServerConstraints api m =
118+ ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
119+ , GServantProduct (Rep (api (AsServerT m)))
120+ )
121+
122+-- | This class is a necessary evil: in the implementation of 'HasServer' for
123+-- @'NamedRoutes' api@, we essentially need the quantified constraint @forall
124+-- m. 'GServerConstraints' m@ to hold.
125+--
126+-- We cannot require do that directly as the definition of 'GServerConstraints'
127+-- contains type family applications ('Rep' and 'ServerT'). The trick is to hide
128+-- those type family applications behind a typeclass providing evidence for
129+-- @'GServerConstraints' api m@ in the form of a dictionary, and require that
130+-- @forall m. 'GServer' api m@ instead.
131+--
132+-- Users shouldn't have to worry about this class, as the only possible instance
133+-- is provided in this module for all record APIs.
134+
135+class GServer (api :: * -> *) (m :: * -> *) where
136+ proof :: Dict (GServerConstraints api m)
137+
138+instance
139+ ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
140+ , GServantProduct (Rep (api (AsServerT m)))
141+ ) => GServer api m where
142+ proof = Dict
143+
144+instance
145+ ( HasServer (ToServantApi api) context
146+ , forall m. Generic (api (AsServerT m))
147+ , forall m. GServer api m
148+ ) => HasServer (NamedRoutes api) context where
149+
150+ type ServerT (NamedRoutes api) m = api (AsServerT m)
151+
152+ route
153+ :: Proxy (NamedRoutes api)
154+ -> Context context
155+ -> Delayed env (api (AsServerT Handler))
156+ -> Router env
157+ route _ ctx delayed =
158+ case proof @api @Handler of
159+ Dict -> route (Proxy @(ToServantApi api)) ctx (toServant <$> delayed)
160+
161+ hoistServerWithContext
162+ :: forall m n. Proxy (NamedRoutes api)
163+ -> Proxy context
164+ -> (forall x. m x -> n x)
165+ -> api (AsServerT m)
166+ -> api (AsServerT n)
167+ hoistServerWithContext _ pctx nat server =
168+ case (proof @api @m, proof @api @n) of
169+ (Dict, Dict) ->
170+ fromServant servantSrvN
171+ where
172+ servantSrvM :: ServerT (ToServantApi api) m =
173+ toServant server
174+ servantSrvN :: ServerT (ToServantApi api) n =
175+ hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM