@@ -41,7 +41,7 @@ import qualified Data.ByteString as B
41import qualified Data.ByteString.Builder as BB
41import qualified Data.ByteString.Builder as BB
42import qualified Data.ByteString.Char8 as BC8
42import qualified Data.ByteString.Char8 as BC8
43import qualified Data.ByteString.Lazy as BL
43import qualified Data.ByteString.Lazy as BL
44−import Data.Constraint (Constraint, Dict(..))
44+import Data.Constraint (Dict(..))
45import Data.Either
45import Data.Either
46 (partitionEithers)
46 (partitionEithers)
47import Data.Maybe
47import Data.Maybe
@@ -56,7 +56,7 @@ import qualified Data.Text as T
56import Data.Typeable
56import Data.Typeable
57import GHC.Generics
57import GHC.Generics
58import GHC.TypeLits
58import GHC.TypeLits
59− (KnownNat, KnownSymbol, TypeError, symbolVal)
59+ (KnownNat, KnownSymbol, symbolVal)
60import qualified Network.HTTP.Media as NHM
60import qualified Network.HTTP.Media as NHM
61import Network.HTTP.Types hiding
61import Network.HTTP.Types hiding
62 (Header, ResponseHeaders)
62 (Header, ResponseHeaders)
@@ -90,7 +90,6 @@ import Servant.API.ResponseHeaders
90import Servant.API.Status
90import Servant.API.Status
91 (statusFromNat)
91 (statusFromNat)
92import qualified Servant.Types.SourceT as S
92import qualified Servant.Types.SourceT as S
93−import Servant.API.TypeErrors
94import Web.HttpApiData
93import Web.HttpApiData
95 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
94 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
96 parseUrlPieces)
95 parseUrlPieces)
@@ -173,7 +172,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
173-- > server = getBook
172-- > server = getBook
174-- > where getBook :: Text -> Handler Book
173-- > where getBook :: Text -> Handler Book
175-- > getBook isbn = ...
174-- > getBook isbn = ...
176−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
175+instance (KnownSymbol capture, FromHttpApiData a
177 , HasServer api context, SBoolI (FoldLenient mods)
176 , HasServer api context, SBoolI (FoldLenient mods)
178 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
177 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -185,7 +184,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
185 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
184 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
187 route Proxy context d =
186 route Proxy context d =
188− CaptureRouter [hint] $
187+ CaptureRouter $
189 route (Proxy :: Proxy api)
188 route (Proxy :: Proxy api)
191 (addCapture d $ \ txt -> withRequest $ \ request ->
190 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -197,7 +196,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
198 rep = typeRep (Proxy :: Proxy Capture')
197 rep = typeRep (Proxy :: Proxy Capture')
199 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
198 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
200− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
202-- | If you use 'CaptureAll' in one of the endpoints for your API,
200-- | If you use 'CaptureAll' in one of the endpoints for your API,
203-- this automatically requires your server-side handler to be a
201-- this automatically requires your server-side handler to be a
@@ -216,7 +214,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
216-- > server = getSourceFile
214-- > server = getSourceFile
217-- > where getSourceFile :: [Text] -> Handler Book
215-- > where getSourceFile :: [Text] -> Handler Book
218-- > getSourceFile pathSegments = ...
216-- > getSourceFile pathSegments = ...
219−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
217+instance (KnownSymbol capture, FromHttpApiData a
220 , HasServer api context
218 , HasServer api context
221 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
219 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -228,7 +226,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
228 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
226 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
230 route Proxy context d =
228 route Proxy context d =
231− CaptureAllRouter [hint] $
229+ CaptureAllRouter $
232 route (Proxy :: Proxy api)
230 route (Proxy :: Proxy api)
234 (addCapture d $ \ txts -> withRequest $ \ request ->
232 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -239,7 +237,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
240 rep = typeRep (Proxy :: Proxy CaptureAll)
238 rep = typeRep (Proxy :: Proxy CaptureAll)
241 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
239 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
242− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
244allowedMethodHead :: Method -> Request -> Bool
241allowedMethodHead :: Method -> Request -> Bool
245allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
242allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -817,15 +814,38 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
817 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
814 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
819-------------------------------------------------------------------------------
816-------------------------------------------------------------------------------
820−-- Custom type errors
817+-- TypeError helpers
821-------------------------------------------------------------------------------
818-------------------------------------------------------------------------------
823−-- Erroring instance for 'HasServer' when a combinator is not fully applied
820+-- | This instance catches mistakes when there are non-saturated
821+-- type applications on LHS of ':>'.
823+-- >>> serve (Proxy :: Proxy (Capture "foo" :> Get '[JSON] Int)) (error "...")
825+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
826+-- ...Maybe you haven't applied enough arguments to
827+-- ...Capture' '[] "foo"
830+-- >>> undefined :: Server (Capture "foo" :> Get '[JSON] Int)
832+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
833+-- ...Maybe you haven't applied enough arguments to
834+-- ...Capture' '[] "foo"
824−instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
837+instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
826− type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
839+ type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
827− route = error "unreachable"
840+ -- it doesn't really matter what sub route we peak
841+ route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
828− hoistServerWithContext _ _ _ _ = error "unreachable"
842+ hoistServerWithContext _ _ _ = id
844+-- Cannot have TypeError here, otherwise use of this symbol will error :)
845+type HasServerArrowKindError arr =
846+ 'Text "Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'."
847+ ':$$: 'Text "Maybe you haven't applied enough arguments to"
830-- | This instance prevents from accidentally using '->' instead of ':>'
850-- | This instance prevents from accidentally using '->' instead of ':>'
@@ -860,15 +880,6 @@ type HasServerArrowTypeError a b =
860 ':$$: 'Text "and"
880 ':$$: 'Text "and"
861 ':$$: 'ShowType b
881 ':$$: 'ShowType b
863−-- Erroring instances for 'HasServer' for unknown API combinators
865−-- XXX: This omits the @context@ parameter, e.g.:
867−-- "There is no instance for HasServer (Bool :> …)". Do we care ?
868−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub HasServer ty) => HasServer (ty :> sub) context
870−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context
872-- | Ignore @'Fragment'@ in server handlers.
883-- | Ignore @'Fragment'@ in server handlers.
873-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
884-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.