@@ -31,12 +31,6 @@ module Servant.Server.Internal
3131 , module Servant.Server.Internal.ServerError
3232 ) where
3333
34−import Data.Proxy (Proxy (Proxy))
35−import Data.SOP (I (I))
36−import Data.SOP.Constraint (All, And)
37−import Data.String.Conversions (LBS, cs)
38−import Network.HTTP.Types (Status, HeaderName, hContentType)
39−
4034import Control.Monad
4135 (join, when)
4236import Control.Monad.Trans
@@ -47,7 +41,7 @@ import qualified Data.ByteString as B
4741import qualified Data.ByteString.Builder as BB
4842import qualified Data.ByteString.Char8 as BC8
4943import qualified Data.ByteString.Lazy as BL
50−import Data.Constraint (Dict(..))
44+import Data.Constraint (Constraint, Dict(..))
5145import Data.Either
5246 (partitionEithers)
5347import Data.Maybe
@@ -82,9 +76,7 @@ import Servant.API
8276 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
8377 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
8478 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
85− WithNamedContext, NamedRoutes, UVerb, WithStatus(..))
86−
87−import Servant.API.UVerb (HasStatus, IsMember, Statuses, UVerb, Union, Unique, WithStatus (..), foldMapUnion, inject, statusOf)
79+ WithNamedContext, NamedRoutes)
8880import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
8981import Servant.API.ContentTypes
9082 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
@@ -96,7 +88,7 @@ import Servant.API.Modifiers
9688import Servant.API.ResponseHeaders
9789 (GetHeaders, Headers, getHeaders, getResponse)
9890import Servant.API.Status
99− (statusFromNat, KnownStatus)
91+ (statusFromNat)
10092import qualified Servant.Types.SourceT as S
10193import Servant.API.TypeErrors
10294import Web.HttpApiData
@@ -181,7 +173,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
181173-- > server = getBook
182174-- > where getBook :: Text -> Handler Book
183175-- > getBook isbn = ...
184−instance (KnownSymbol capture, FromHttpApiData a
176+instance (KnownSymbol capture, FromHttpApiData a, Typeable a
185177 , HasServer api context, SBoolI (FoldLenient mods)
186178 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
187179 )
@@ -193,7 +185,7 @@ instance (KnownSymbol capture, FromHttpApiData a
193185 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
194186
195187 route Proxy context d =
196− CaptureRouter $
188+ CaptureRouter [hint] $
197189 route (Proxy :: Proxy api)
198190 context
199191 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -205,6 +197,7 @@ instance (KnownSymbol capture, FromHttpApiData a
205197 where
206198 rep = typeRep (Proxy :: Proxy Capture')
207199 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
200+ hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
208201
209202-- | If you use 'CaptureAll' in one of the endpoints for your API,
210203-- this automatically requires your server-side handler to be a
@@ -223,7 +216,7 @@ instance (KnownSymbol capture, FromHttpApiData a
223216-- > server = getSourceFile
224217-- > where getSourceFile :: [Text] -> Handler Book
225218-- > getSourceFile pathSegments = ...
226−instance (KnownSymbol capture, FromHttpApiData a
219+instance (KnownSymbol capture, FromHttpApiData a, Typeable a
227220 , HasServer api context
228221 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
229222 )
@@ -235,7 +228,7 @@ instance (KnownSymbol capture, FromHttpApiData a
235228 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
236229
237230 route Proxy context d =
238− CaptureAllRouter $
231+ CaptureAllRouter [hint] $
239232 route (Proxy :: Proxy api)
240233 context
241234 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -246,6 +239,7 @@ instance (KnownSymbol capture, FromHttpApiData a
246239 where
247240 rep = typeRep (Proxy :: Proxy CaptureAll)
248241 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
242+ hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
249243
250244allowedMethodHead :: Method -> Request -> Bool
251245allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -300,36 +294,28 @@ noContentRouter method status action = leafRouter route'
300294 env request respond $ \ _output ->
301295 Route $ responseLBS status [] ""
302296
303−-- instance {-# OVERLAPPABLE #-}
304−-- ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
305−-- ) => HasServer (Verb method status ctypes a) context where
306−
307−-- type ServerT (Verb method status ctypes a) m = m a
308−-- hoistServerWithContext _ _ nt s = nt s
309−
310−-- route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
311−-- where method = reflectMethod (Proxy :: Proxy method)
312−-- status = statusFromNat (Proxy :: Proxy status)
297+instance {-# OVERLAPPABLE #-}
298+ ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
299+ ) => HasServer (Verb method status ctypes a) context where
313300
314−-- instance {-# OVERLAPPING #-}
315−-- ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
316−-- , GetHeaders (Headers h a)
317−-- ) => HasServer (Verb method status ctypes (Headers h a)) context where
301+ type ServerT (Verb method status ctypes a) m = m a
302+ hoistServerWithContext _ _ nt s = nt s
318303
319−-- type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
320−-- hoistServerWithContext _ _ nt s = nt s
304+ route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
305+ where method = reflectMethod (Proxy :: Proxy method)
306+ status = statusFromNat (Proxy :: Proxy status)
321307
322−-- route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
323−-- where method = reflectMethod (Proxy :: Proxy method)
324−-- status = statusFromNat (Proxy :: Proxy status)
308+instance {-# OVERLAPPING #-}
309+ ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
310+ , GetHeaders (Headers h a)
311+ ) => HasServer (Verb method status ctypes (Headers h a)) context where
325312
326−instance
327− (KnownStatus statusCode, HasServer (UVerb method ctypes '[WithStatus statusCode a]) context) =>
328− HasServer (Verb method statusCode ctypes a) context where
313+ type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
314+ hoistServerWithContext _ _ nt s = nt s
329315
330− type ServerT (Verb method statusCode ctypes a) m = m a
331− route = undefined
332− hoistServerWithContext p1 p2 nat s = undefined
316+ route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
317+ where method = reflectMethod (Proxy :: Proxy method)
318+ status = statusFromNat (Proxy :: Proxy status)
333319
334320instance (ReflectMethod method) =>
335321 HasServer (NoContentVerb method) context where
@@ -837,7 +823,7 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
837823-- Erroring instance for 'HasServer' when a combinator is not fully applied
838824instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
839825 where
840− type ServerT (arr :> sub) _ = TypeError (PartialApplication HasServer arr)
826+ type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
841827 route = error "unreachable"
842828 hoistServerWithContext _ _ _ _ = error "unreachable"
843829
@@ -973,84 +959,3 @@ instance
973959 toServant server
974960 servantSrvN :: ServerT (ToServantApi api) n =
975961 hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM
976−
977−
978−
979−-- | 'return' for 'UVerb' handlers. Takes a value of any of the members of the open union,
980−-- and will construct a union value in an 'Applicative' (eg. 'Server').
981−respond ::
982− forall (x :: *) (xs :: [*]) (f :: * -> *).
983− (Applicative f, HasStatus x, IsMember x xs) =>
984− x ->
985− f (Union xs)
986−respond = pure . inject . I
987−
988−class IsServerResource (cts :: [*]) a where
989− resourceResponse :: Request -> Proxy cts -> a -> Maybe (LBS, LBS)
990− resourceHeaders :: Proxy cts -> a -> [(HeaderName, B.ByteString)]
991−
992−instance {-# OVERLAPPABLE #-} AllCTRender cts a
993− => IsServerResource cts a where
994− resourceResponse request p res = handleAcceptH p (getAcceptHeader request) res
995− resourceHeaders _ _ = []
996−
997−instance {-# OVERLAPPING #-} (IsServerResource cts a, GetHeaders (Headers h a))
998− => IsServerResource cts (Headers h a) where
999− resourceResponse request p res = resourceResponse request p (getResponse res)
1000− resourceHeaders cts res = getHeaders res ++ resourceHeaders cts (getResponse res)
1001−
1002−instance {-# OVERLAPPING #-} IsServerResource cts a
1003− => IsServerResource cts (WithStatus n a) where
1004− resourceResponse request p (WithStatus x) = resourceResponse request p x
1005− resourceHeaders cts (WithStatus x) = resourceHeaders cts x
1006−
1007−encodeResource :: forall a cts . (IsServerResource cts a, HasStatus a)
1008− => Request -> Proxy cts -> a
1009− -> (Status, Maybe (LBS, LBS), [(HeaderName, B.ByteString)])
1010−encodeResource request cts res = (statusOf (Proxy @a),
1011− resourceResponse request cts res,
1012− resourceHeaders cts res)
1013−
1014−type IsServerResourceWithStatus cts = IsServerResource cts `And` HasStatus
1015−
1016−instance
1017− ( ReflectMethod method,
1018− AllMime contentTypes,
1019− All (IsServerResourceWithStatus contentTypes) as,
1020− Unique (Statuses as) -- for consistency with servant-swagger (server would work fine
1021− -- without; client is a bit of a corner case, because it dispatches
1022− -- the parser based on the status code. with this uniqueness
1023− -- constraint it won't have to run more than one parser in weird
1024− -- corner cases.
1025− ) =>
1026− HasServer (UVerb method contentTypes as) context
1027− where
1028− type ServerT (UVerb method contentTypes as) m = m (Union as)
1029−
1030− hoistServerWithContext _ _ nt s = nt s
1031−
1032− route ::
1033− forall env.
1034− Proxy (UVerb method contentTypes as) ->
1035− Context context ->
1036− Delayed env (Server (UVerb method contentTypes as)) ->
1037− Router env
1038− route _proxy _ctx action = leafRouter route'
1039− where
1040− method = reflectMethod (Proxy @method)
1041− route' env request cont = do
1042− let action' :: Delayed env (Handler (Union as))
1043− action' =
1044− action
1045− `addMethodCheck` methodCheck method request
1046− `addAcceptCheck` acceptCheck (Proxy @contentTypes) (getAcceptHeader request)
1047−
1048− runAction action' env request cont $ \(output :: Union as) -> do
1049− let cts = Proxy @contentTypes
1050− pickResource :: Union as -> (Status, Maybe (LBS, LBS), [(HeaderName, B.ByteString)])
1051− pickResource = foldMapUnion (Proxy @(IsServerResourceWithStatus contentTypes)) (encodeResource request cts)
1052− case pickResource output of
1053− (_, Nothing, _) -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does
1054− (status, Just (contentT, body), headers) ->
1055− let bdy = if allowedMethodHead method request then "" else body
1056− in Route $ responseLBS status ((hContentType, cs contentT) : headers) bdy