on master help sign in
Add ‘HttpBodyAllowed’ type family
2f0598dmrkkrp
Network/HTTP/Req.hs+39−11
@@ -97,6 +97,10 @@
97{-# LANGUAGE ScopedTypeVariables #-}
97{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
98{-# LANGUAGE TypeFamilies #-}
99
99
100+#if MIN_VERSION_base(4,9,0)
101+{-# LANGUAGE UndecidableInstances #-}
102+#endif
103+
100#if __GLASGOW_HASKELL__ >= 800
104#if __GLASGOW_HASKELL__ >= 800
101{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
105{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
102#endif
106#endif
@@ -143,6 +147,7 @@ module Network.HTTP.Req
143 , FormUrlEncodedParam
147 , FormUrlEncodedParam
144 , HttpBody (..)
148 , HttpBody (..)
145 , ProvidesBody
149 , ProvidesBody
150+ , HttpBodyAllowed
146 -- ** Optional parameters
151 -- ** Optional parameters
147 -- $optional-parameters
152 -- $optional-parameters
148 , Option
153 , Option
@@ -211,7 +216,6 @@ import Data.List (nubBy)
211import Data.List.NonEmpty (NonEmpty (..))
216import Data.List.NonEmpty (NonEmpty (..))
212import Data.Proxy
217import Data.Proxy
213import Data.Semigroup hiding (Option, option)
218import Data.Semigroup hiding (Option, option)
214−import Data.Text (Text)
215import Data.Typeable (Typeable)
219import Data.Typeable (Typeable)
216import GHC.Generics
220import GHC.Generics
217import GHC.TypeLits
221import GHC.TypeLits
@@ -224,6 +228,7 @@ import qualified Data.ByteString as B
224import qualified Data.ByteString.Lazy as BL
228import qualified Data.ByteString.Lazy as BL
225import qualified Data.CaseInsensitive as CI
229import qualified Data.CaseInsensitive as CI
226import qualified Data.List.NonEmpty as NE
230import qualified Data.List.NonEmpty as NE
231+import qualified Data.Text as T
227import qualified Data.Text.Encoding as T
232import qualified Data.Text.Encoding as T
228import qualified Network.Connection as NC
233import qualified Network.Connection as NC
229import qualified Network.HTTP.Client as L
234import qualified Network.HTTP.Client as L
@@ -233,6 +238,12 @@ import qualified Network.HTTP.Req.AWS as AWS
233import qualified Network.HTTP.Req.OAuth1 as OAuth1
238import qualified Network.HTTP.Req.OAuth1 as OAuth1
234import qualified Network.HTTP.Types as Y
239import qualified Network.HTTP.Types as Y
235
240
241+#if MIN_VERSION_base(4,9,0)
242+import Data.Kind (Constraint)
243+#else
244+import GHC.Exts (Constraint)
245+#endif
246+
236----------------------------------------------------------------------------
247----------------------------------------------------------------------------
237-- Making a request
248-- Making a request
238
249
@@ -369,7 +380,7 @@ req
369 , HttpMethod method
380 , HttpMethod method
370 , HttpBody body
381 , HttpBody body
371 , HttpResponse response
382 , HttpResponse response
372− , AllowsBody method ~ ProvidesBody body )
383+ , HttpBodyAllowed (AllowsBody method) (ProvidesBody body) )
373 => method -- ^ HTTP method
384 => method -- ^ HTTP method
374 -> Url scheme -- ^ 'Url' — location of resource
385 -> Url scheme -- ^ 'Url' — location of resource
375 -> body -- ^ Body of the request
386 -> body -- ^ Body of the request
@@ -657,20 +668,20 @@ instance HttpMethod method => RequestComponent (Womb "method" method) where
657-- > https "юникод.рф"
668-- > https "юникод.рф"
658-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
669-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
659
670
660−data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
671+data Url (scheme :: Scheme) = Url Scheme (NonEmpty T.Text)
661 -- NOTE The second value is path segments in reversed order.
672 -- NOTE The second value is path segments in reversed order.
662 deriving (Eq, Ord, Show, Data, Typeable, Generic)
673 deriving (Eq, Ord, Show, Data, Typeable, Generic)
663
674
664-- | Given host name, produce a 'Url' which have “http” as its scheme and
675-- | Given host name, produce a 'Url' which have “http” as its scheme and
665-- empty path. This also sets port to @80@.
676-- empty path. This also sets port to @80@.
666
677
667−http :: Text -> Url 'Http
678+http :: T.Text -> Url 'Http
668http = Url Http . pure
679http = Url Http . pure
669
680
670-- | Given host name, produce a 'Url' which have “https” as its scheme and
681-- | Given host name, produce a 'Url' which have “https” as its scheme and
671-- empty path. This also sets port to @443@.
682-- empty path. This also sets port to @443@.
672
683
673−https :: Text -> Url 'Https
684+https :: T.Text -> Url 'Https
674https = Url Https . pure
685https = Url Https . pure
675
686
676-- | Grow given 'Url' appending a single path segment to it. Note that the
687-- | Grow given 'Url' appending a single path segment to it. Note that the
@@ -684,7 +695,7 @@ Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
684-- next URL piece is a 'Text' literal.
695-- next URL piece is a 'Text' literal.
685
696
686infixl 5 /:
697infixl 5 /:
687−(/:) :: Url scheme -> Text -> Url scheme
698+(/:) :: Url scheme -> T.Text -> Url scheme
688(/:) = (/~)
699(/:) = (/~)
689
700
690-- | The 'parseUrlHttp' function provides an alternative method to get 'Url'
701-- | The 'parseUrlHttp' function provides an alternative method to get 'Url'
@@ -715,7 +726,7 @@ parseUrlHttps url' = do
715-- | Get host\/collection of path pieces and possibly query parameters
726-- | Get host\/collection of path pieces and possibly query parameters
716-- already converted to 'Option'. This function is not public.
727-- already converted to 'Option'. This function is not public.
717
728
718−parseUrlHelper :: ByteString -> Maybe (NonEmpty Text, Option scheme)
729+parseUrlHelper :: ByteString -> Maybe (NonEmpty T.Text, Option scheme)
719parseUrlHelper url = do
730parseUrlHelper url = do
720 let (path', query') = B.break (== 0x3f) url
731 let (path', query') = B.break (== 0x3f) url
721 query = mconcat (uncurry queryParam <$> Y.parseQueryText query')
732 query = mconcat (uncurry queryParam <$> Y.parseQueryText query')
@@ -819,7 +830,7 @@ instance HttpBody ReqBodyUrlEnc where
819-- | An opaque monoidal value that allows to collect URL-encoded parameters
830-- | An opaque monoidal value that allows to collect URL-encoded parameters
820-- to be wrapped in 'ReqBodyUrlEnc'.
831-- to be wrapped in 'ReqBodyUrlEnc'.
821
832
822−newtype FormUrlEncodedParam = FormUrlEncodedParam [(Text, Maybe Text)]
833+newtype FormUrlEncodedParam = FormUrlEncodedParam [(T.Text, Maybe T.Text)]
823 deriving (Semigroup, Monoid)
834 deriving (Semigroup, Monoid)
824
835
825instance QueryParam FormUrlEncodedParam where
836instance QueryParam FormUrlEncodedParam where
@@ -852,6 +863,23 @@ type family ProvidesBody body :: CanHaveBody where
852 ProvidesBody NoReqBody = 'NoBody
863 ProvidesBody NoReqBody = 'NoBody
853 ProvidesBody body = 'CanHaveBody
864 ProvidesBody body = 'CanHaveBody
854
865
866+-- | This type function allows any HTTP body if method says it
867+-- 'CanHaveBody'. When method says it should have 'NoBody', the only body
868+-- option to use is 'NoReqBody'.
869+--
870+-- __Note__: users of GHC 8.0.1 will see slightly more friendly error
871+-- messages when method does not allow a body and body is provided.
872+
873+type family HttpBodyAllowed
874+ (allowsBody :: CanHaveBody)
875+ (providesBody :: CanHaveBody) :: Constraint where
876+ HttpBodyAllowed 'NoBody 'NoBody = ()
877+ HttpBodyAllowed 'CanHaveBody body = ()
878+#if MIN_VERSION_base(4,9,0)
879+ HttpBodyAllowed 'NoBody 'CanHaveBody = TypeError
880+ ('Text "This HTTP method does not allow attaching a request body.")
881+#endif
882+
855instance HttpBody body => RequestComponent (Womb "body" body) where
883instance HttpBody body => RequestComponent (Womb "body" body) where
856 getRequestMod (Womb body) = Endo $ \x ->
884 getRequestMod (Womb body) = Endo $ \x ->
857 x { L.requestBody = getRequestBody body
885 x { L.requestBody = getRequestBody body
@@ -935,7 +963,7 @@ instance RequestComponent (Option scheme) where
935-- > name =: value = queryParam name (pure value)
963-- > name =: value = queryParam name (pure value)
936
964
937infix 7 =:
965infix 7 =:
938−(=:) :: (QueryParam param, ToHttpApiData a) => Text -> a -> param
966+(=:) :: (QueryParam param, ToHttpApiData a) => T.Text -> a -> param
939name =: value = queryParam name (pure value)
967name =: value = queryParam name (pure value)
940
968
941-- | Construct a flag, that is, valueless query parameter. For example, in
969-- | Construct a flag, that is, valueless query parameter. For example, in
@@ -947,7 +975,7 @@ name =: value = queryParam name (pure value)
947--
975--
948-- > queryFlag name = queryParam name Nothing
976-- > queryFlag name = queryParam name Nothing
949
977
950−queryFlag :: QueryParam param => Text -> param
978+queryFlag :: QueryParam param => T.Text -> param
951queryFlag name = queryParam name (Nothing :: Maybe ())
979queryFlag name = queryParam name (Nothing :: Maybe ())
952
980
953-- | A type class for query-parameter-like things. The reason to have
981-- | A type class for query-parameter-like things. The reason to have
@@ -962,7 +990,7 @@ class QueryParam param where
962 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
990 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
963 -- method, because they are easier to read.
991 -- method, because they are easier to read.
964
992
965− queryParam :: ToHttpApiData a => Text -> Maybe a -> param
993+ queryParam :: ToHttpApiData a => T.Text -> Maybe a -> param
966
994
967instance QueryParam (Option scheme) where
995instance QueryParam (Option scheme) where
968 queryParam name mvalue =
996 queryParam name mvalue =