11 -------------------------------------------------------------------------------------------------
19 , class ParsePathPiece
26import Bun.Request (Request)
27import Bun.Request as Request
28import Bun.Response (Response)
29import Bun.Response as Response
30import Control.Alternative (class Plus, empty)
31import Control.Monad.Except (ExceptT, runExceptT)
32import Data.Argonaut (class DecodeJson, class EncodeJson, decodeJson, encodeJson)
33import Data.Array as Array
34import Data.Either (Either(..))
38import Data.Maybe (Maybe(..))
39import Data.Number as Number
40import Data.String as String
41import Data.Symbol (class IsSymbol, reflectSymbol)
42import Data.Traversable (traverse)
44import Effect.Aff (Aff)
45import Effect.Aff.Class (liftAff)
47import Prim.RowList (class RowToList, RowList)
48import Prim.RowList as RowList
49import Record as Record
50import Record.Builder (Builder)
51import Record.Builder as Builder
52import Type.Proxy (Proxy(..))
54type Route = { path :: Array String, verb :: String, query :: Map String (Array String) }
56routeFromRequest :: Request -> Route
59 url = URL.fromString $ Request.url req
61 { verb: Request.method req
64 Just u -> case URL.path u of
66 URL.PathAbsolute p -> p
67 URL.PathRelative p -> p
73type Server a = ExceptT Response Aff a
75newtype Nt m = Nt (forall x. m x -> Server x)
77class ServeRoute :: forall k. k -> Type -> (Type -> Type) -> Constraint
78class ServeRoute route handler m | route -> handler, handler -> m where
79 serveRoute :: Route -> Maybe (Nt m -> handler -> Request -> Server Response)
81instance (ServeRoute rest handler m, IsSymbol path) => ServeRoute (path / rest) handler m where
82 serveRoute route@{ path } = do
83 { head: h, tail } <- Array.uncons path
84 assert $ h == (reflectSymbol @path Proxy)
85 serveRoute @rest route { path = tail }
88 ( ServeRoute rest handler m
92 ServeRoute (Record row / rest) (Record row -> handler) m where
93 serveRoute route@{ query } = do
94 builder <- serveQuery @row @list query
95 serveRoute @rest route <#> (\f nt handler req -> f nt (handler $ Builder.buildFromScratch builder) req)
97else instance (ServeRoute rest handler m, ParsePathPiece t) => ServeRoute (t / rest) (t -> handler) m where
98 serveRoute route@{ path } = do
99 { head: h, tail } <- Array.uncons path
100 t <- parsePathPiece @t h
101 serveRoute @rest route { path = tail } <#> (\f nt handler req -> f nt (handler t) req)
103else instance ToResponse resp => ServeRoute (GET resp) (m resp) m where
104 serveRoute { path, verb } = do
105 assert $ verb == "GET"
107 pure $ \(Nt nt) handler _req -> do
109 pure $ toResponse resp
111else instance ServeRoute (POST Unit Unit) (m Unit) m where
112 serveRoute { path, verb } = do
113 assert $ verb == "POST"
115 pure $ \(Nt nt) handler _req -> do
118else instance (ToResponse resp) => ServeRoute (POST Unit resp) (m resp) m where
119 serveRoute { path, verb } = do
120 assert $ verb == "POST"
122 pure $ \(Nt nt) handler _req -> do
124 pure $ toResponse resp
125else instance (FromRequest req) => ServeRoute (POST resp Unit) (req -> m Unit) m where
126 serveRoute { path, verb } = do
127 assert $ verb == "POST"
129 pure $ \(Nt nt) handler req -> do
130 liftAff (fromRequest req) >>= case _ of
131 Nothing -> pure $ badRequest
135else instance (FromRequest req, ToResponse resp) => ServeRoute (POST req resp) (req -> m resp) m where
136 serveRoute { path, verb } = do
137 assert $ verb == "POST"
139 pure $ \(Nt nt) handler req -> do
140 liftAff (fromRequest req) >>= case _ of
141 Nothing -> pure $ badRequest
143 resp <- nt $ handler r
144 pure $ toResponse resp
146else instance ServeRoute (DELETE Unit Unit) (m Unit) m where
147 serveRoute { path, verb } = do
148 assert $ verb == "DELETE"
150 pure $ \(Nt nt) handler _req -> do
153else instance (ToResponse resp) => ServeRoute (DELETE Unit resp) (m resp) m where
154 serveRoute { path, verb } = do
155 assert $ verb == "DELETE"
157 pure $ \(Nt nt) handler _req -> do
159 pure $ toResponse resp
160else instance (FromRequest req) => ServeRoute (DELETE resp Unit) (req -> m Unit) m where
161 serveRoute { path, verb } = do
162 assert $ verb == "DELETE"
164 pure $ \(Nt nt) handler req -> do
165 liftAff (fromRequest req) >>= case _ of
166 Nothing -> pure $ badRequest
170else instance (FromRequest req, ToResponse resp) => ServeRoute (DELETE req resp) (req -> m resp) m where
171 serveRoute { path, verb } = do
172 assert $ verb == "DELETE"
174 pure $ \(Nt nt) handler req -> do
175 liftAff (fromRequest req) >>= case _ of
176 Nothing -> pure $ badRequest
178 resp <- nt $ handler r
179 pure $ toResponse resp
181else instance ServeRoute (PUT Unit Unit) (m Unit) m where
182 serveRoute { path, verb } = do
183 assert $ verb == "PUT"
185 pure $ \(Nt nt) handler _req -> do
188else instance (ToResponse resp) => ServeRoute (PUT Unit resp) (m resp) m where
189 serveRoute { path, verb } = do
190 assert $ verb == "PUT"
192 pure $ \(Nt nt) handler _req -> do
194 pure $ toResponse resp
195else instance (FromRequest req) => ServeRoute (PUT resp Unit) (req -> m Unit) m where
196 serveRoute { path, verb } = do
197 assert $ verb == "PUT"
199 pure $ \(Nt nt) handler req -> do
200 liftAff (fromRequest req) >>= case _ of
201 Nothing -> pure $ badRequest
205else instance (FromRequest req, ToResponse resp) => ServeRoute (PUT req resp) (req -> m resp) m where
206 serveRoute { path, verb } = do
207 assert $ verb == "PUT"
209 pure $ \(Nt nt) handler req -> do
210 liftAff (fromRequest req) >>= case _ of
211 Nothing -> pure $ badRequest
213 resp <- nt $ handler r
214 pure $ toResponse resp
216else instance ServeRoute (PATCH Unit Unit) (m Unit) m where
217 serveRoute { path, verb } = do
218 assert $ verb == "PATCH"
220 pure $ \(Nt nt) handler _req -> do
223else instance (ToResponse resp) => ServeRoute (PATCH Unit resp) (m resp) m where
224 serveRoute { path, verb } = do
225 assert $ verb == "PATCH"
227 pure $ \(Nt nt) handler _req -> do
229 pure $ toResponse resp
230else instance (FromRequest req) => ServeRoute (PATCH resp Unit) (req -> m Unit) m where
231 serveRoute { path, verb } = do
232 assert $ verb == "PATCH"
234 pure $ \(Nt nt) handler req -> do
235 liftAff (fromRequest req) >>= case _ of
236 Nothing -> pure $ badRequest
240else instance (FromRequest req, ToResponse resp) => ServeRoute (PATCH req resp) (req -> m resp) m where
241 serveRoute { path, verb } = do
242 assert $ verb == "PATCH"
244 pure $ \(Nt nt) handler req -> do
245 liftAff (fromRequest req) >>= case _ of
246 Nothing -> pure $ badRequest
248 resp <- nt $ handler r
249 pure $ toResponse resp
252 ( ServeAPI routes list handlers m
253 , RowToList routes list
255 ServeRoute routes (Record handlers) m where
256 serveRoute route = pure $ \nt handlers req ->
257 serveAPI @routes @list nt handlers route req
259class ServeQuery :: Row Type -> RowList Type -> Constraint
260class ServeQuery row list | list -> row where
261 serveQuery :: Map String (Array String) -> Maybe (Builder (Record ()) (Record row))
263instance ServeQuery () RowList.Nil where
264 serveQuery _ = Just $ identity
268 , ServeQuery tail rest
270 , Row.Cons field (Array t) tail row
271 , Row.Lacks field tail
273 ServeQuery row (RowList.Cons field (Array t) rest) where
275 r <- serveQuery @tail @rest m
276 case Map.lookup (reflectSymbol @field Proxy) m of
277 Nothing -> pure $ Builder.insert (Proxy @field) [] <<< r
279 v <- parsePathPiece @t `traverse` a
280 pure $ Builder.insert (Proxy @field) v <<< r
284 , ServeQuery tail rest
286 , Row.Cons field (Maybe t) tail row
287 , Row.Lacks field tail
289 ServeQuery row (RowList.Cons field (Maybe t) rest) where
291 r <- serveQuery @tail @rest m
292 case Map.lookup (reflectSymbol @field Proxy) m of
293 Nothing -> pure $ Builder.insert (Proxy @field) Nothing <<< r
295 { head, tail } <- Array.uncons a
297 v <- parsePathPiece @t head
298 pure $ Builder.insert (Proxy @field) (Just v) <<< r
302 , ServeQuery tail rest
304 , Row.Cons field t tail row
305 , Row.Lacks field tail
307 ServeQuery row (RowList.Cons field t rest) where
309 { head, tail } <- Map.lookup (reflectSymbol @field Proxy) m >>= Array.uncons
311 v <- parsePathPiece @t head
312 r <- serveQuery @tail @rest m
313 pure $ Builder.insert (Proxy @field) v <<< r
315class ToResponse r where
316 toResponse :: r -> Response
318instance EncodeJson (Record row) => ToResponse (Record row) where
319 toResponse rec = Response.json (encodeJson rec) { status: 200, statusText: "OK", headers: [] }
321instance ToResponse Response where
322 toResponse resp = resp
324class FromRequest r where
325 fromRequest :: Request -> Aff (Maybe r)
327instance DecodeJson (Record row) => FromRequest (Record row) where
328 fromRequest req = Request.json req <#> \j -> case decodeJson j of
332instance FromRequest Request where
333 fromRequest req = pure $ Just req
335class ParsePathPiece t where
336 parsePathPiece :: String -> Maybe t
338instance ParsePathPiece String where
339 parsePathPiece = Just
341instance ParsePathPiece Int where
342 parsePathPiece = Int.fromString
344instance ParsePathPiece Number where
345 parsePathPiece = Number.fromString
347instance ParsePathPiece Boolean where
348 parsePathPiece s = case String.toLower s of
351 "false" -> Just false
355assert :: forall m. Applicative m => Plus m => Boolean -> m Unit
356assert true = pure unit
359class ServeAPI :: Row Type -> RowList Type -> Row Type -> (Type -> Type) -> Constraint
360class ServeAPI row list handlers m | row -> handlers, handlers -> m where
361 serveAPI :: Nt m -> Record handlers -> Route -> Request -> Server Response
363instance ServeAPI row RowList.Nil () m where
364 serveAPI _ _ _ _ = pure notFound
367 ( ServeRoute t handler m
368 , Row.Cons field t tail row
369 , ServeAPI row rest restHandlers m
371 , Row.Cons field handler restHandlers handlers
372 , Row.Lacks field restHandlers
374 ServeAPI row (RowList.Cons field t rest) handlers m where
375 serveAPI nt handlers route req =
376 case serveRoute @t route of
377 Nothing -> serveAPI @row @rest @restHandlers nt (Record.delete (Proxy @field) handlers) route req
378 Just f -> f nt (Record.get (Proxy @field) handlers) req
381notFound = Response.string "" { status: 404, statusText: "Not Found", headers: [] }
383internalServerError :: Response
384internalServerError = Response.string "" { status: 500, statusText: "Internal Server Error", headers: [] }
386badRequest :: Response
387badRequest = Response.string "" { status: 400, statusText: "Bad Request", headers: [] }
390ok = Response.string "" { status: 200, statusText: "OK", headers: [] }
392-- | The first argument is used to run an arbitrary monad in handlers. To use the 'Server' monad
393-- | instead you can just pass `identity` as the first argument.
395 :: forall @row list handlers m
397 => ServeAPI row list handlers m
398 => (forall x. m x -> Server x)
402run nt handlers req = do
403 res <- runExceptT $ serveAPI @row @list @handlers (Nt nt) handlers (routeFromRequest req) req
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