1 {-# LANGUAGE DefaultSignatures #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE StrictData #-}
4 {-# LANGUAGE TypeApplications #-}
5 {-# LANGUAGE TypeFamilies #-}
6 {-# LANGUAGE TypeOperators #-}
7 module Symantic.HTTP.API
8 ( module Symantic.HTTP.API
12 import Prelude (and, pure)
13 import Data.Eq (Eq(..))
14 -- import Data.Maybe (Maybe(..))
15 import Data.Ord (Ord(..))
16 import Data.Proxy (Proxy(..))
17 import Data.String (String)
18 import Text.Show (Show(..))
19 import qualified Data.ByteString as BS
20 import qualified Data.Text as T
21 import qualified Network.HTTP.Types as HTTP
22 import qualified Web.HttpApiData as Web
24 import Symantic.HTTP.Media
25 import Symantic.HTTP.Mime
37 -- , HTTP_ContentType repr
40 ) => HTTP_API (repr:: * -> * -> *)
44 (<.>) :: repr a b -> repr b c -> repr a c; infixl 4 <.>
45 -- (.>) :: repr x y -> repr a c -> repr a c; infixl 4 .>
50 type AltMerge repr :: * -> * -> *
51 (<!>) :: repr a b -> repr c d -> repr (a:!:c) (AltMerge repr b d); infixl 3 <!>
53 (<!>) :: repr a k -> repr b k -> repr (a:!:b) k; infixl 3 <!>
54 -- try :: repr k k -> repr k k
55 -- option :: k -> repr k k -> repr k k
58 -- Like '(,)' but 'infixl'.
59 data (:!:) a b = a:!:b
63 -- | Mainly useful to write a combinator which a specialization of another,
64 -- by calling it instead of rewriting its logic.
65 -- Because 'a' is asked in a client but given in a server,
66 -- both @a->b@ and @b->a@ are used. This is reminiscent of a 'Profunctor'.
67 -- Hence the names 'Pro' and 'dimap'.
69 dimap :: (a -> b) -> (b -> a) -> repr (a -> k) k -> repr (b -> k) k
71 -- * Class 'HTTP_Path'
72 class HTTP_Path repr where
73 segment :: Segment -> repr k k
75 Web.FromHttpApiData a =>
76 Web.ToHttpApiData a =>
77 Name -> repr (a -> k) k
78 captureAll :: repr ([Segment] -> k) k
80 -- | Like |capture'| but with the type variable 'a' first instead or 'repr'
81 -- so it can be passed using 'TypeApplications' without adding a '@_' for 'repr'.
85 Web.FromHttpApiData a =>
86 Web.ToHttpApiData a =>
87 Name -> repr (a -> k) k
89 {-# INLINE capture #-}
95 -- * Class 'HTTP_Method'
96 class HTTP_Method repr where
97 method :: HTTP.Method -> repr k k
98 method_GET :: repr k k
99 method_POST :: repr k k
100 method_HEAD :: repr k k
101 method_PUT :: repr k k
102 method_DELETE :: repr k k
103 method_TRACE :: repr k k
104 method_CONNECT :: repr k k
105 method_OPTIONS :: repr k k
106 method_PATCH :: repr k k
107 method_GET = method HTTP.methodGet
108 method_HEAD = method HTTP.methodHead
109 method_PUT = method HTTP.methodPut
110 method_POST = method HTTP.methodPost
111 method_DELETE = method HTTP.methodDelete
112 method_TRACE = method HTTP.methodTrace
113 method_CONNECT = method HTTP.methodConnect
114 method_OPTIONS = method HTTP.methodOptions
115 method_PATCH = method HTTP.methodPatch
117 -- * Class 'HTTP_Header'
118 class HTTP_Header repr where
119 header :: HTTP.HeaderName -> repr (HeaderValue -> k) k
120 type HeaderValue = BS.ByteString
122 -- * Class 'HTTP_Body'
123 class HTTP_Body repr where
124 type BodyArg repr :: * -> * -> *
127 MimeUnserialize mt a =>
128 MimeSerialize mt a =>
129 repr (BodyArg repr mt a -> k) k
131 -- | Like |body'| but with the type variables 'a' and 'mt' first instead or 'repr'
132 -- so it can be passed using 'TypeApplications' withs adding a '@_' for 'repr'.
136 MimeUnserialize mt a =>
137 MimeSerialize mt a =>
138 repr (BodyArg repr mt a -> k) k
139 body = body' @repr @mt
142 -- * Class 'HTTP_Accept'
143 class HTTP_Accept repr where
144 accept :: MediaTypeable mt => Proxy mt -> repr k k
146 acceptCase :: Functor repr => Alt repr => [AcceptResponse repr a] -> repr BSL.ByteString
147 acceptCase [] = tina $> BSL.empty
148 acceptCase (AcceptResponse (mt,r) : rs) = try (accept mt r) <+> acceptCase rs
151 data AcceptResponse repr a =
152 forall mt. MimeSerialize mt a =>
153 AcceptResponse (Proxy mt, repr a)
155 -- * Class 'HTTP_Content'
156 class HTTP_ContentType repr where
157 contentType :: MediaTypeable mt => Proxy mt -> repr k k
159 -- * Class 'HTTP_Query'
160 class HTTP_Query repr where
162 Web.FromHttpApiData a =>
163 Web.ToHttpApiData a =>
164 QueryName -> repr ([a] -> k) k
165 queryFlag :: QueryName -> repr (Bool -> k) k
166 default queryFlag :: Pro repr => QueryName -> repr (Bool -> k) k
167 queryFlag n = dimap and pure (queryParams' n)
168 type QueryName = BS.ByteString
169 type QueryValue = BS.ByteString
174 Web.FromHttpApiData a =>
175 Web.ToHttpApiData a =>
176 QueryName -> repr ([a] -> k) k
177 queryParams = queryParams'
178 {-# INLINE queryParams #-}
180 -- * Class 'HTTP_Version'
181 class HTTP_Version repr where
182 version :: HTTP.HttpVersion -> repr k k
184 -- * Class 'HTTP_Status'
185 class HTTP_Status repr where
186 status :: StatusIs -> repr (HTTP.Status -> k) k
188 -- ** Type 'StatusIs'
190 = StatusIsInformational
192 | StatusIsRedirection
193 | StatusIsClientError
194 | StatusIsServerError
195 | StatusIs HTTP.Status
196 deriving (Eq, Ord, Show)
197 statusIs :: StatusIs -> (HTTP.Status -> Bool)
199 StatusIsInformational -> HTTP.statusIsInformational
200 StatusIsSuccessful -> HTTP.statusIsSuccessful
201 StatusIsRedirection -> HTTP.statusIsRedirection
202 StatusIsClientError -> HTTP.statusIsClientError
203 StatusIsServerError -> HTTP.statusIsServerError
204 StatusIs x -> \y -> HTTP.statusCode x == HTTP.statusCode y
206 status200 :: HTTP.Status
207 status200 = HTTP.mkStatus 200 "Success"
208 status400 :: HTTP.Status
209 status400 = HTTP.mkStatus 400 "Bad Request"
210 status404 :: HTTP.Status
211 status404 = HTTP.mkStatus 404 "Not Found"
212 status405 :: HTTP.Status
213 status405 = HTTP.mkStatus 405 "Method Not Allowed"
214 status406 :: HTTP.Status
215 status406 = HTTP.mkStatus 406 "Not Acceptable"
216 status415 :: HTTP.Status
217 status415 = HTTP.mkStatus 415 "Unsupported Media Type"
219 -- * Class 'HTTP_Response'
220 class HTTP_Response repr where
221 type Response repr :: *
222 type ResponseArg repr :: * -> * -> *
224 MimeUnserialize mt a =>
225 MimeSerialize mt a =>
228 repr (ResponseArg repr mt a -> k) k
230 -- | Like |response'| but with the type variables 'a' and 'mt' first instead or 'repr'
231 -- so it can be passed using 'TypeApplications' withs adding a '@_' for 'repr'.
234 HTTP_Response repr =>
235 MimeUnserialize mt a =>
236 MimeSerialize mt a =>
239 repr (ResponseArg repr mt a -> k) k
241 {-# INLINE response #-}