1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DataKinds #-}
3 {-# LANGUAGE DefaultSignatures #-}
4 {-# LANGUAGE DeriveFunctor #-}
5 {-# LANGUAGE StrictData #-}
6 {-# LANGUAGE TypeApplications #-}
7 {-# LANGUAGE TypeFamilies #-}
8 {-# LANGUAGE TypeOperators #-}
9 module Symantic.HTTP.API where
12 import Data.Eq (Eq(..))
13 import Data.Functor (Functor)
14 import Data.Kind (Constraint)
15 import Data.Ord (Ord(..))
16 import Data.String (String)
17 import Data.Text (Text)
18 import Prelude (and, pure)
19 import Text.Show (Show(..))
20 import qualified Data.ByteString as BS
21 import qualified Network.HTTP.Types as HTTP
34 ) => HTTP_API (repr:: * -> * -> *)
38 (<.>) :: repr a b -> repr b c -> repr a c; infixl 4 <.>
39 -- (.>) :: repr x y -> repr a c -> repr a c; infixl 4 .>
44 type AltMerge repr :: * -> * -> *
45 (<!>) :: repr a b -> repr c d -> repr (a:!:c) (AltMerge repr b d); infixl 3 <!>
47 (<!>) :: repr a k -> repr b k -> repr (a:!:b) k; infixl 3 <!>
48 -- try :: repr k k -> repr k k
49 -- option :: k -> repr k k -> repr k k
52 -- Like '(,)' but 'infixl'.
53 -- Used to get alternative commands from a 'Client'
54 -- or to supply alternative handlers to a 'Server'.
55 data (:!:) a b = a:!:b
59 -- | Mainly useful to write a combinator which is a specialization of another,
60 -- by calling it instead of rewriting its logic.
61 -- Because 'a' si asked by a 'Client' but given to a 'Server',
62 -- both @a->b@ and @b->a@ are used. This is reminiscent of a 'Profunctor'.
63 -- Hence the names 'Pro' and 'dimap'.
65 dimap :: (a -> b) -> (b -> a) -> repr (a -> k) k -> repr (b -> k) k
67 -- * Class 'NoConstraint'
68 -- | A placeholder 'Constraint' which has an instance for any type.
69 -- Useful for 'HTTP_BasicAuth'.
71 instance NoConstraint a
73 -- * Type family 'HttpApiData'
74 -- | Either 'Web.ToHttpApiData' for a 'Client',
75 -- or 'Web.FromHttpApiData' for a 'Server'.
76 type family HttpApiData (repr:: * -> * -> *) :: * -> Constraint
78 -- * Type family 'MimeCodable'
79 -- | Either 'MimeEncodable' or 'MimeDecodable'.
80 type family MimeCodable (repr:: * -> * -> *) :: * -> * -> Constraint
82 -- * Class 'HTTP_Path'
83 class HTTP_Path repr where
84 segment :: Segment -> repr k k
87 Name -> repr (a -> k) k
88 captureAll :: repr ([Segment] -> k) k
90 -- | Convenient wrapper of 'segment'.
91 (</>) :: Cat repr => HTTP_Path repr => Segment -> repr a b -> repr a b
92 (</>) n = (segment n <.>); infixr 5 </>
94 -- | Like |capture'| but with the type variable 'a' first instead or 'repr'
95 -- so it can be passed using 'TypeApplications' without adding a '@_' for 'repr'.
100 Name -> repr (a -> k) k
102 {-# INLINE capture #-}
105 type Path = [Segment]
108 -- * Class 'HTTP_Header'
109 class HTTP_Header repr where
110 header :: HTTP.HeaderName -> repr (HeaderValue -> k) k
112 type HeaderValue = BS.ByteString
114 -- * Class 'HTTP_Body'
115 class HTTP_Body repr where
116 type BodyArg repr :: * -> [*] -> *
117 type BodyConstraint repr a (ts::[*]) :: Constraint
119 forall a (ts::[*]) k.
120 BodyConstraint repr a ts =>
121 repr (BodyArg repr a ts -> k) k
123 -- | Like |body'| but with the type variables 'a' and 'ts' first instead or 'repr'
124 -- so it can be passed using 'TypeApplications' withs adding a '@_' for 'repr'.
128 BodyConstraint repr a ts =>
129 repr (BodyArg repr a ts -> k) k
133 -- * Class 'HTTP_Query'
134 class HTTP_Query repr where
136 HttpApiData repr a =>
137 QueryName -> repr ([a] -> k) k
139 HttpApiData repr Bool =>
140 QueryName -> repr (Bool -> k) k
143 HttpApiData repr Bool =>
144 QueryName -> repr (Bool -> k) k
145 queryFlag n = dimap and pure (queryParams' n)
146 type QueryName = BS.ByteString
147 type QueryValue = BS.ByteString
152 HttpApiData repr a =>
153 QueryName -> repr ([a] -> k) k
154 queryParams = queryParams'
155 {-# INLINE queryParams #-}
157 -- * Class 'HTTP_Auth'
158 -- | <https://tools.ietf.org/html/rfc2617#section-2 Basic Access Authentication>
159 class HTTP_BasicAuth repr where
160 type BasicAuthConstraint repr :: * -> Constraint
161 type BasicAuthConstraint repr = NoConstraint
162 type BasicAuthArgs repr a k :: *
164 BasicAuthConstraint repr a =>
165 BasicAuthRealm -> repr (BasicAuthArgs repr a k) k
169 HTTP_BasicAuth repr =>
170 BasicAuthConstraint repr a =>
172 repr (BasicAuthArgs repr a k) k
173 basicAuth = basicAuth' @repr @a @k
174 {-# INLINE basicAuth #-}
176 -- ** Type 'BasicAuth'
178 = BasicAuth_Authorized usr
179 | BasicAuth_BadPassword
180 | BasicAuth_NoSuchUser
181 | BasicAuth_Unauthorized
182 deriving (Eq, Show, Functor)
184 type BasicAuthRealm = Text
185 type BasicAuthUser = Text
186 type BasicAuthPass = Text
188 -- * Class 'HTTP_Version'
189 class HTTP_Version repr where
190 version :: HTTP.HttpVersion -> repr k k
192 -- * Class 'HTTP_Status'
193 class HTTP_Status repr where
194 status :: StatusIs -> repr (HTTP.Status -> k) k
196 -- ** Type 'StatusIs'
198 = StatusIsInformational
200 | StatusIsRedirection
201 | StatusIsClientError
202 | StatusIsServerError
203 | StatusIs HTTP.Status
204 deriving (Eq, Ord, Show)
205 statusIs :: StatusIs -> (HTTP.Status -> Bool)
207 StatusIsInformational -> HTTP.statusIsInformational
208 StatusIsSuccessful -> HTTP.statusIsSuccessful
209 StatusIsRedirection -> HTTP.statusIsRedirection
210 StatusIsClientError -> HTTP.statusIsClientError
211 StatusIsServerError -> HTTP.statusIsServerError
212 StatusIs x -> \y -> HTTP.statusCode x == HTTP.statusCode y
214 -- * Class 'HTTP_Response'
215 class HTTP_Response repr where
216 type ResponseConstraint repr a (ts::[*]) :: Constraint
217 type ResponseArgs repr a (ts::[*]) :: *
218 type Response repr a (ts::[*]) :: *
220 ResponseConstraint repr a ts =>
222 repr (ResponseArgs repr a ts)
225 -- | Wrap 'response' by giving it the corresponding 'HTTP.Method',
226 -- and put the type variables 'a' then 'ts' first instead or 'repr'
227 -- so they can be passed using 'TypeApplications'
228 -- without adding a '@_' for 'repr'.
229 get,head,put,post,delete,trace,connect,options,patch ::
231 HTTP_Response repr =>
232 ResponseConstraint repr a ts =>
233 repr (ResponseArgs repr a ts)
235 get = response @repr @a @ts HTTP.methodGet
236 head = response @repr @a @ts HTTP.methodHead
237 put = response @repr @a @ts HTTP.methodPut
238 post = response @repr @a @ts HTTP.methodPost
239 delete = response @repr @a @ts HTTP.methodDelete
240 trace = response @repr @a @ts HTTP.methodTrace
241 connect = response @repr @a @ts HTTP.methodConnect
242 options = response @repr @a @ts HTTP.methodOptions
243 patch = response @repr @a @ts HTTP.methodPatch
248 {-# INLINE delete #-}
250 {-# INLINE connect #-}
251 {-# INLINE options #-}