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
251class ServeQuery :: Row Type -> RowList Type -> Constraint
252class ServeQuery row list | list -> row where
253 serveQuery :: Map String (Array String) -> Maybe (Builder (Record ()) (Record row))
255instance ServeQuery () RowList.Nil where
256 serveQuery _ = Just $ identity
260 , ServeQuery tail rest
262 , Row.Cons field (Array t) tail row
263 , Row.Lacks field tail
265 ServeQuery row (RowList.Cons field (Array t) rest) where
267 r <- serveQuery @tail @rest m
268 case Map.lookup (reflectSymbol @field Proxy) m of
269 Nothing -> pure $ Builder.insert (Proxy @field) [] <<< r
271 v <- parsePathPiece @t `traverse` a
272 pure $ Builder.insert (Proxy @field) v <<< r
276 , ServeQuery tail rest
278 , Row.Cons field (Maybe t) tail row
279 , Row.Lacks field tail
281 ServeQuery row (RowList.Cons field (Maybe t) rest) where
283 r <- serveQuery @tail @rest m
284 case Map.lookup (reflectSymbol @field Proxy) m of
285 Nothing -> pure $ Builder.insert (Proxy @field) Nothing <<< r
287 { head, tail } <- Array.uncons a
289 v <- parsePathPiece @t head
290 pure $ Builder.insert (Proxy @field) (Just v) <<< r
294 , ServeQuery tail rest
296 , Row.Cons field t tail row
297 , Row.Lacks field tail
299 ServeQuery row (RowList.Cons field t rest) where
301 { head, tail } <- Map.lookup (reflectSymbol @field Proxy) m >>= Array.uncons
303 v <- parsePathPiece @t head
304 r <- serveQuery @tail @rest m
305 pure $ Builder.insert (Proxy @field) v <<< r
307class ToResponse r where
308 toResponse :: r -> Response
310instance EncodeJson (Record row) => ToResponse (Record row) where
311 toResponse rec = Response.json (encodeJson rec) { status: 200, statusText: "OK", headers: [] }
313instance ToResponse Response where
314 toResponse resp = resp
316class FromRequest r where
317 fromRequest :: Request -> Aff (Maybe r)
319instance DecodeJson (Record row) => FromRequest (Record row) where
320 fromRequest req = Request.json req <#> \j -> case decodeJson j of
324instance FromRequest Request where
325 fromRequest req = pure $ Just req
327class ParsePathPiece t where
328 parsePathPiece :: String -> Maybe t
330instance ParsePathPiece String where
331 parsePathPiece = Just
333instance ParsePathPiece Int where
334 parsePathPiece = Int.fromString
336instance ParsePathPiece Number where
337 parsePathPiece = Number.fromString
339instance ParsePathPiece Boolean where
340 parsePathPiece s = case String.toLower s of
343 "false" -> Just false
347assert :: forall m. Applicative m => Plus m => Boolean -> m Unit
348assert true = pure unit
351class ServeAPI :: Row Type -> RowList Type -> Row Type -> (Type -> Type) -> Constraint
352class ServeAPI row list handlers m | row -> handlers, handlers -> m where
353 serveAPI :: Nt m -> Record handlers -> Route -> Request -> Server Response
355instance ServeAPI row RowList.Nil () m where
356 serveAPI _ _ _ _ = pure notFound
359 ( ServeRoute t handler m
360 , Row.Cons field t tail row
361 , ServeAPI row rest restHandlers m
363 , Row.Cons field handler restHandlers handlers
364 , Row.Lacks field restHandlers
366 ServeAPI row (RowList.Cons field t rest) handlers m where
367 serveAPI nt handlers route req =
368 case serveRoute @t route of
369 Nothing -> serveAPI @row @rest @restHandlers nt (Record.delete (Proxy @field) handlers) route req
370 Just f -> f nt (Record.get (Proxy @field) handlers) req
373notFound = Response.string "" { status: 404, statusText: "Not Found", headers: [] }
375internalServerError :: Response
376internalServerError = Response.string "" { status: 500, statusText: "Internal Server Error", headers: [] }
378badRequest :: Response
379badRequest = Response.string "" { status: 400, statusText: "Bad Request", headers: [] }
382ok = Response.string "" { status: 200, statusText: "OK", headers: [] }
384-- | The first argument is used to run an arbitrary monad in handlers. To use the 'Server' monad
385-- | instead you can just pass `identity` as the first argument.
387 :: forall @row list handlers m
389 => ServeAPI row list handlers m
390 => (forall x. m x -> Server x)
394run nt handlers req = do
395 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