13 -------------------------------------------------------------------------------------------------
21 , class ParsePathPiece
28import Bun.Request (Request)
29import Bun.Request as Request
30import Bun.Response as Bun.Response
31import Control.Alternative (class Plus, empty)
32import Control.Monad.Except (ExceptT, runExceptT)
33import Data.Argonaut (class DecodeJson, class EncodeJson, Json, decodeJson, encodeJson)
34import Data.Array as Array
35import Data.Either (Either(..), either)
39import Data.Maybe (Maybe(..))
40import Data.Number as Number
41import Data.String as String
42import Data.Symbol (class IsSymbol, reflectSymbol)
43import Data.Traversable (traverse)
45import Effect.Aff (Aff)
46import Effect.Aff.Class (liftAff)
47import Foreign.Object (Object)
48import Foreign.Object as Object
50import Prim.RowList (class RowToList, RowList)
51import Prim.RowList as RowList
52import Record as Record
53import Record.Builder (Builder)
54import Record.Builder as Builder
55import Type.Proxy (Proxy(..))
57type Route = { path :: Array String, verb :: String, query :: Map String (Array String) }
59routeFromRequest :: Request -> Route
62 url = URL.fromString $ Request.url req
64 { verb: Request.method req
67 Just u -> case URL.path u of
69 URL.PathAbsolute p -> p
70 URL.PathRelative p -> p
76type Server a = ExceptT Response Aff a
78newtype Nt m = Nt (forall x. m x -> Server x)
80class ServeRoute :: forall k. k -> Type -> (Type -> Type) -> Constraint
81class ServeRoute route handler m | route -> handler, handler -> m where
82 serveRoute :: Route -> Maybe (Nt m -> handler -> Request -> Server Response)
84instance (ServeRoute rest handler m, IsSymbol path) => ServeRoute (path / rest) handler m where
85 serveRoute route@{ path } = do
86 { head: h, tail } <- Array.uncons path
87 assert $ h == (reflectSymbol @path Proxy)
88 serveRoute @rest route { path = tail }
91 ( ServeRoute rest handler m
95 ServeRoute (Record row / rest) (Record row -> handler) m where
96 serveRoute route@{ query } = do
97 builder <- serveQuery @row @list query
98 serveRoute @rest route <#> (\f nt handler req -> f nt (handler $ Builder.buildFromScratch builder) req)
100else instance (ServeRoute rest handler m, ParsePathPiece t) => ServeRoute (t / rest) (t -> handler) m where
101 serveRoute route@{ path } = do
102 { head: h, tail } <- Array.uncons path
103 t <- parsePathPiece @t h
104 serveRoute @rest route { path = tail } <#> (\f nt handler req -> f nt (handler t) req)
106else instance ToResponse resp => ServeRoute (GET resp) (m resp) m where
107 serveRoute { path, verb } = do
108 assert $ verb == "GET"
110 pure $ \(Nt nt) handler _req -> do
112 pure $ toResponse resp
114else instance ServeRoute (POST Unit Unit) (m Unit) m where
115 serveRoute { path, verb } = do
116 assert $ verb == "POST"
118 pure $ \(Nt nt) handler _req -> do
121else instance (ToResponse resp) => ServeRoute (POST Unit resp) (m resp) m where
122 serveRoute { path, verb } = do
123 assert $ verb == "POST"
125 pure $ \(Nt nt) handler _req -> do
127 pure $ toResponse resp
128else instance (FromRequest req) => ServeRoute (POST resp Unit) (req -> m Unit) m where
129 serveRoute { path, verb } = do
130 assert $ verb == "POST"
132 pure $ \(Nt nt) handler req -> do
133 liftAff (fromRequest req) >>= case _ of
134 Nothing -> pure $ badRequest
138else instance (FromRequest req, ToResponse resp) => ServeRoute (POST req resp) (req -> m resp) m where
139 serveRoute { path, verb } = do
140 assert $ verb == "POST"
142 pure $ \(Nt nt) handler req -> do
143 liftAff (fromRequest req) >>= case _ of
144 Nothing -> pure $ badRequest
146 resp <- nt $ handler r
147 pure $ toResponse resp
149else instance ServeRoute (DELETE Unit Unit) (m Unit) m where
150 serveRoute { path, verb } = do
151 assert $ verb == "DELETE"
153 pure $ \(Nt nt) handler _req -> do
156else instance (ToResponse resp) => ServeRoute (DELETE Unit resp) (m resp) m where
157 serveRoute { path, verb } = do
158 assert $ verb == "DELETE"
160 pure $ \(Nt nt) handler _req -> do
162 pure $ toResponse resp
163else instance (FromRequest req) => ServeRoute (DELETE resp Unit) (req -> m Unit) m where
164 serveRoute { path, verb } = do
165 assert $ verb == "DELETE"
167 pure $ \(Nt nt) handler req -> do
168 liftAff (fromRequest req) >>= case _ of
169 Nothing -> pure $ badRequest
173else instance (FromRequest req, ToResponse resp) => ServeRoute (DELETE req resp) (req -> m resp) m where
174 serveRoute { path, verb } = do
175 assert $ verb == "DELETE"
177 pure $ \(Nt nt) handler req -> do
178 liftAff (fromRequest req) >>= case _ of
179 Nothing -> pure $ badRequest
181 resp <- nt $ handler r
182 pure $ toResponse resp
184else instance ServeRoute (PUT Unit Unit) (m Unit) m where
185 serveRoute { path, verb } = do
186 assert $ verb == "PUT"
188 pure $ \(Nt nt) handler _req -> do
191else instance (ToResponse resp) => ServeRoute (PUT Unit resp) (m resp) m where
192 serveRoute { path, verb } = do
193 assert $ verb == "PUT"
195 pure $ \(Nt nt) handler _req -> do
197 pure $ toResponse resp
198else instance (FromRequest req) => ServeRoute (PUT resp Unit) (req -> m Unit) m where
199 serveRoute { path, verb } = do
200 assert $ verb == "PUT"
202 pure $ \(Nt nt) handler req -> do
203 liftAff (fromRequest req) >>= case _ of
204 Nothing -> pure $ badRequest
208else instance (FromRequest req, ToResponse resp) => ServeRoute (PUT req resp) (req -> m resp) m where
209 serveRoute { path, verb } = do
210 assert $ verb == "PUT"
212 pure $ \(Nt nt) handler req -> do
213 liftAff (fromRequest req) >>= case _ of
214 Nothing -> pure $ badRequest
216 resp <- nt $ handler r
217 pure $ toResponse resp
219else instance ServeRoute (PATCH Unit Unit) (m Unit) m where
220 serveRoute { path, verb } = do
221 assert $ verb == "PATCH"
223 pure $ \(Nt nt) handler _req -> do
226else instance (ToResponse resp) => ServeRoute (PATCH Unit resp) (m resp) m where
227 serveRoute { path, verb } = do
228 assert $ verb == "PATCH"
230 pure $ \(Nt nt) handler _req -> do
232 pure $ toResponse resp
233else instance (FromRequest req) => ServeRoute (PATCH resp Unit) (req -> m Unit) m where
234 serveRoute { path, verb } = do
235 assert $ verb == "PATCH"
237 pure $ \(Nt nt) handler req -> do
238 liftAff (fromRequest req) >>= case _ of
239 Nothing -> pure $ badRequest
243else instance (FromRequest req, ToResponse resp) => ServeRoute (PATCH req resp) (req -> m resp) m where
244 serveRoute { path, verb } = do
245 assert $ verb == "PATCH"
247 pure $ \(Nt nt) handler req -> do
248 liftAff (fromRequest req) >>= case _ of
249 Nothing -> pure $ badRequest
251 resp <- nt $ handler r
252 pure $ toResponse resp
255 ( ServeAPI routes list handlers m
256 , RowToList routes list
258 ServeRoute routes (Record handlers) m where
259 serveRoute route = pure $ \nt handlers req ->
260 serveAPI @routes @list nt handlers route req
262class ServeQuery :: Row Type -> RowList Type -> Constraint
263class ServeQuery row list | list -> row where
264 serveQuery :: Map String (Array String) -> Maybe (Builder (Record ()) (Record row))
266instance ServeQuery () RowList.Nil where
267 serveQuery _ = Just $ identity
271 , ServeQuery tail rest
273 , Row.Cons field (Array t) tail row
274 , Row.Lacks field tail
276 ServeQuery row (RowList.Cons field (Array t) rest) where
278 r <- serveQuery @tail @rest m
279 case Map.lookup (reflectSymbol @field Proxy) m of
280 Nothing -> pure $ Builder.insert (Proxy @field) [] <<< r
282 v <- parsePathPiece @t `traverse` a
283 pure $ Builder.insert (Proxy @field) v <<< r
287 , ServeQuery tail rest
289 , Row.Cons field (Maybe t) tail row
290 , Row.Lacks field tail
292 ServeQuery row (RowList.Cons field (Maybe t) rest) where
294 r <- serveQuery @tail @rest m
295 case Map.lookup (reflectSymbol @field Proxy) m of
296 Nothing -> pure $ Builder.insert (Proxy @field) Nothing <<< r
298 { head, tail } <- Array.uncons a
300 v <- parsePathPiece @t head
301 pure $ Builder.insert (Proxy @field) (Just v) <<< r
305 , ServeQuery tail rest
307 , Row.Cons field t tail row
308 , Row.Lacks field tail
310 ServeQuery row (RowList.Cons field t rest) where
312 { head, tail } <- Map.lookup (reflectSymbol @field Proxy) m >>= Array.uncons
314 v <- parsePathPiece @t head
315 r <- serveQuery @tail @rest m
316 pure $ Builder.insert (Proxy @field) v <<< r
319 = JsonResponseBody Json
321 | StringResponseBody String
323newtype Response = Response { body :: ResponseBody, status :: Int, statusText :: String, headers :: Object String }
325class ToResponse r where
326 toResponse :: r -> Response
328instance EncodeJson (Record row) => ToResponse (Record row) where
329 toResponse rec = Response { body: JsonResponseBody $ encodeJson rec, status: 200, statusText: "OK", headers: Object.empty }
331instance ToResponse Response where
332 toResponse resp = resp
334class FromRequest r where
335 fromRequest :: Request -> Aff (Maybe r)
337instance DecodeJson (Record row) => FromRequest (Record row) where
338 fromRequest req = Request.json req <#> \j -> case decodeJson j of
342instance FromRequest Request where
343 fromRequest req = pure $ Just req
345class ParsePathPiece t where
346 parsePathPiece :: String -> Maybe t
348instance ParsePathPiece String where
349 parsePathPiece = Just
351instance ParsePathPiece Int where
352 parsePathPiece = Int.fromString
354instance ParsePathPiece Number where
355 parsePathPiece = Number.fromString
357instance ParsePathPiece Boolean where
358 parsePathPiece s = case String.toLower s of
361 "false" -> Just false
365assert :: forall m. Applicative m => Plus m => Boolean -> m Unit
366assert true = pure unit
369class ServeAPI :: Row Type -> RowList Type -> Row Type -> (Type -> Type) -> Constraint
370class ServeAPI row list handlers m | row -> handlers, handlers -> m where
371 serveAPI :: Nt m -> Record handlers -> Route -> Request -> Server Response
373instance ServeAPI row RowList.Nil () m where
374 serveAPI _ _ _ _ = pure notFound
377 ( ServeRoute t handler m
378 , Row.Cons field t tail row
379 , ServeAPI row rest restHandlers m
381 , Row.Cons field handler restHandlers handlers
382 , Row.Lacks field restHandlers
384 ServeAPI row (RowList.Cons field t rest) handlers m where
385 serveAPI nt handlers route req =
386 case serveRoute @t route of
387 Nothing -> serveAPI @row @rest @restHandlers nt (Record.delete (Proxy @field) handlers) route req
388 Just f -> f nt (Record.get (Proxy @field) handlers) req
391notFound = Response { body: EmptyResponseBody, status: 404, statusText: "Not Found", headers: Object.empty }
393internalServerError :: Response
394internalServerError = Response { body: EmptyResponseBody, status: 500, statusText: "Internal Server Error", headers: Object.empty }
396badRequest :: Response
397badRequest = Response { body: EmptyResponseBody, status: 400, statusText: "Bad Request", headers: Object.empty }
400ok = Response { body: EmptyResponseBody, status: 200, statusText: "OK", headers: Object.empty }
402-- | The first argument is used to run an arbitrary monad in handlers. To use the 'Server' monad
403-- | instead you can just pass `identity` as the first argument.
405 :: forall @row list handlers m
407 => ServeAPI row list handlers m
408 => (forall x. m x -> Server x)
411 -> Aff Bun.Response.Response
412run nt handlers req = do
413 Response { body, headers, status, statusText } <-
414 map (either identity identity) $ runExceptT $ serveAPI @row @list @handlers (Nt nt) handlers (routeFromRequest req) req
415 let opts = { headers, status, statusText }
417 EmptyResponseBody -> Bun.Response.string "" opts
418 JsonResponseBody json -> Bun.Response.json json opts
419 StringResponseBody string -> Bun.Response.string string opts
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list