@@ -33,9 +33,7 @@ import Control.Arrow
3333import Control.Monad
3434 (unless)
3535import qualified Data.ByteString as BS
36−import Data.ByteString.Builder
37− (toLazyByteString)
38−import qualified Data.ByteString.Lazy as BL
36+import qualified Data.ByteString.Lazy as BL
3937import Data.Either
4038 (partitionEithers)
4139import Data.Constraint (Dict(..))
@@ -65,7 +63,7 @@ import Data.Text
6563import Data.Proxy
6664 (Proxy (Proxy))
6765import GHC.TypeLits
68− (KnownSymbol, symbolVal)
66+ (KnownNat, KnownSymbol, TypeError, symbolVal)
6967import Network.HTTP.Types
7068 (Status)
7169import qualified Network.HTTP.Types as H
@@ -86,9 +84,12 @@ import Servant.API.Generic
8684 , GenericServant, toServant, fromServant)
8785import Servant.API.ContentTypes
8886 (contentTypes, AllMime (allMime), AllMimeUnrender (allMimeUnrender))
87+import Servant.API.Status
88+ (statusFromNat)
8989import Servant.API.TypeLevel (FragmentUnique, AtLeastOneFragment)
9090import Servant.API.Modifiers
9191 (FoldRequired, RequiredArgument, foldRequiredArgument)
92+import Servant.API.TypeErrors
9293import Servant.API.UVerb
9394 (HasStatus, HasStatuses (Statuses, statuses), UVerb, Union, Unique, inject, statusOf, foldMapUnion, matchUnion)
9495
@@ -207,7 +208,7 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient m api)
207208 clientWithRoute pm (Proxy :: Proxy api)
208209 (appendToPath p req)
209210
210− where p = (toUrlPiece val)
211+ where p = toEncodedUrlPiece val
211212
212213 hoistClientMonad pm _ f cl = \a ->
213214 hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
@@ -242,7 +243,7 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient m sublayout)
242243 clientWithRoute pm (Proxy :: Proxy sublayout)
243244 (foldl' (flip appendToPath) req ps)
244245
245− where ps = map (toUrlPiece) vals
246+ where ps = map toEncodedUrlPiece vals
246247
247248 hoistClientMonad pm _ f cl = \as ->
248249 hoistClientMonad pm (Proxy :: Proxy sublayout) f (cl as)
@@ -250,10 +251,11 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient m sublayout)
250251instance {-# OVERLAPPABLE #-}
251252 -- Note [Non-Empty Content Types]
252253 ( RunClient m, MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
254+ , KnownNat status
253255 ) => HasClient m (Verb method status cts' a) where
254256 type Client m (Verb method status cts' a) = m a
255257 clientWithRoute _pm Proxy req = do
256− response <- runRequest req
258+ response <- runRequestAcceptStatus (Just [status]) req
257259 { requestAccept = fromList $ toList accept
258260 , requestMethod = method
259261 }
@@ -261,18 +263,20 @@ instance {-# OVERLAPPABLE #-}
261263 where
262264 accept = contentTypes (Proxy :: Proxy ct)
263265 method = reflectMethod (Proxy :: Proxy method)
266+ status = statusFromNat (Proxy :: Proxy status)
264267
265268 hoistClientMonad _ _ f ma = f ma
266269
267270instance {-# OVERLAPPING #-}
268− ( RunClient m, ReflectMethod method
271+ ( RunClient m, ReflectMethod method, KnownNat status
269272 ) => HasClient m (Verb method status cts NoContent) where
270273 type Client m (Verb method status cts NoContent)
271274 = m NoContent
272275 clientWithRoute _pm Proxy req = do
273− _response <- runRequest req { requestMethod = method }
276+ _response <- runRequestAcceptStatus (Just [status]) req { requestMethod = method }
274277 return NoContent
275278 where method = reflectMethod (Proxy :: Proxy method)
279+ status = statusFromNat (Proxy :: Proxy status)
276280
277281 hoistClientMonad _ _ f ma = f ma
278282
@@ -289,13 +293,13 @@ instance (RunClient m, ReflectMethod method) =>
289293
290294instance {-# OVERLAPPING #-}
291295 -- Note [Non-Empty Content Types]
292− ( RunClient m, MimeUnrender ct a, BuildHeadersTo ls
296+ ( RunClient m, MimeUnrender ct a, BuildHeadersTo ls, KnownNat status
293297 , ReflectMethod method, cts' ~ (ct ': cts)
294298 ) => HasClient m (Verb method status cts' (Headers ls a)) where
295299 type Client m (Verb method status cts' (Headers ls a))
296300 = m (Headers ls a)
297301 clientWithRoute _pm Proxy req = do
298− response <- runRequest req
302+ response <- runRequestAcceptStatus (Just [status]) req
299303 { requestMethod = method
300304 , requestAccept = fromList $ toList accept
301305 }
@@ -303,22 +307,26 @@ instance {-# OVERLAPPING #-}
303307 return $ Headers { getResponse = val
304308 , getHeadersHList = buildHeadersTo . toList $ responseHeaders response
305309 }
306− where method = reflectMethod (Proxy :: Proxy method)
307− accept = contentTypes (Proxy :: Proxy ct)
310+ where
311+ method = reflectMethod (Proxy :: Proxy method)
312+ accept = contentTypes (Proxy :: Proxy ct)
313+ status = statusFromNat (Proxy :: Proxy status)
308314
309315 hoistClientMonad _ _ f ma = f ma
310316
311317instance {-# OVERLAPPING #-}
312− ( RunClient m, BuildHeadersTo ls, ReflectMethod method
318+ ( RunClient m, BuildHeadersTo ls, ReflectMethod method, KnownNat status
313319 ) => HasClient m (Verb method status cts (Headers ls NoContent)) where
314320 type Client m (Verb method status cts (Headers ls NoContent))
315321 = m (Headers ls NoContent)
316322 clientWithRoute _pm Proxy req = do
317− let method = reflectMethod (Proxy :: Proxy method)
318− response <- runRequest req { requestMethod = method }
323+ response <- runRequestAcceptStatus (Just [status]) req { requestMethod = method }
319324 return $ Headers { getResponse = NoContent
320325 , getHeadersHList = buildHeadersTo . toList $ responseHeaders response
321326 }
327+ where
328+ method = reflectMethod (Proxy :: Proxy method)
329+ status = statusFromNat (Proxy :: Proxy status)
322330
323331 hoistClientMonad _ _ f ma = f ma
324332
@@ -561,7 +569,7 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequire
561569 (Proxy :: Proxy mods) add (maybe req add) mparam
562570 where
563571 add :: a -> Request
564− add param = appendToQueryString pname (Just $ encodeQueryParam param) req
572+ add param = appendToQueryString pname (Just $ encodeQueryParamValue param) req
565573
566574 pname :: Text
567575 pname = pack $ symbolVal (Proxy :: Proxy sym)
@@ -569,9 +577,6 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequire
569577 hoistClientMonad pm _ f cl = \arg ->
570578 hoistClientMonad pm (Proxy :: Proxy api) f (cl arg)
571579
572−encodeQueryParam :: ToHttpApiData a => a -> BS.ByteString
573−encodeQueryParam = BL.toStrict . toLazyByteString . toEncodedUrlPiece
574−
575580-- | If you use a 'QueryParams' in one of your endpoints in your API,
576581-- the corresponding querying function will automatically take
577582-- an additional argument, a list of values of the type specified
@@ -613,7 +618,7 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api)
613618 )
614619
615620 where pname = pack $ symbolVal (Proxy :: Proxy sym)
616− paramlist' = map (Just . encodeQueryParam) paramlist
621+ paramlist' = map (Just . encodeQueryParamValue) paramlist
617622
618623 hoistClientMonad pm _ f cl = \as ->
619624 hoistClientMonad pm (Proxy :: Proxy api) f (cl as)
@@ -735,7 +740,7 @@ instance (KnownSymbol path, HasClient m api) => HasClient m (path :> api) where
735740 clientWithRoute pm (Proxy :: Proxy api)
736741 (appendToPath p req)
737742
738− where p = pack $ symbolVal (Proxy :: Proxy path)
743+ where p = toEncodedUrlPiece $ pack $ symbolVal (Proxy :: Proxy path)
739744
740745 hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
741746
@@ -784,7 +789,7 @@ instance ( HasClient m api
784789
785790-- | Ignore @'Fragment'@ in client functions.
786791-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
787−--
792+--
788793-- Example:
789794--
790795-- > type MyApi = "books" :> Fragment Text :> Get '[JSON] [Book]
@@ -801,7 +806,7 @@ instance (AtLeastOneFragment api, FragmentUnique (Fragment a :> api), HasClient
801806
802807 type Client m (Fragment a :> api) = Client m api
803808
804− clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api)
809+ clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api)
805810
806811 hoistClientMonad pm _ = hoistClientMonad pm (Proxy :: Proxy api)
807812
@@ -869,7 +874,7 @@ infixl 2 /:
869874--
870875-- Example:
871876--
872−-- @@
877+-- @
873878-- type Api = NamedRoutes RootApi
874879--
875880-- data RootApi mode = RootApi
@@ -890,7 +895,7 @@ infixl 2 /:
890895--
891896-- endpointClient :: ClientM Person
892897-- endpointClient = client // subApi // endpoint
893−-- @@
898+-- @
894899(//) :: a -> (a -> b) -> b
895900x // f = f x
896901
@@ -901,7 +906,7 @@ x // f = f x
901906--
902907-- Example:
903908--
904−-- @@
909+-- @
905910-- type Api = NamedRoutes RootApi
906911--
907912-- data RootApi mode = RootApi
@@ -926,7 +931,7 @@ x // f = f x
926931--
927932-- endpointClient :: ClientM Person
928933-- endpointClient = client // subApi /: "foobar123" // endpoint
929−-- @@
934+-- @
930935(/:) :: (a -> b -> c) -> b -> a -> c
931936(/:) = flip
932937
@@ -970,3 +975,19 @@ decodedAs response ct = do
970975 Right val -> return val
971976 where
972977 accept = toList $ contentTypes ct
978+
979+-------------------------------------------------------------------------------
980+-- Custom type errors
981+-------------------------------------------------------------------------------
982+
983+-- Erroring instance for HasClient' when a combinator is not fully applied
984+instance (RunClient m, TypeError (PartialApplication HasClient arr)) => HasClient m ((arr :: a -> b) :> sub)
985+ where
986+ type Client m (arr :> sub) = TypeError (PartialApplication HasClient arr)
987+ clientWithRoute _ _ _ = error "unreachable"
988+ hoistClientMonad _ _ _ _ = error "unreachable"
989+
990+-- Erroring instances for 'HasClient' for unknown API combinators
991+instance {-# OVERLAPPABLE #-} (RunClient m, TypeError (NoInstanceForSub (HasClient m) ty)) => HasClient m (ty :> sub)
992+
993+instance {-# OVERLAPPABLE #-} (RunClient m, TypeError (NoInstanceFor (HasClient m api))) => HasClient m api