import gleam/bool import gleam/dynamic import gleam/json import gleam/result import gleam/string_builder import glitr import glitr_wisp/errors import wisp pub type Router { Router(req: wisp.Request) } pub fn for(req: wisp.Request) -> Result(Router, wisp.Response) { Ok(Router(req)) } pub fn try( router_res: Result(Router, wisp.Response), route: glitr.Route(p, q, b, res), handler: fn(glitr.RouteOptions(p, q, b)) -> Result(res, errors.AppError), ) -> Result(Router, wisp.Response) { use router <- result.try(router_res) let result = receive(router.req, route, handler) case result { Ok(response) -> Error(response) Error(Nil) -> Ok(router) } } pub fn try_map( router_res: Result(Router, wisp.Response), route: glitr.Route(p, q, b, res), handler: fn(glitr.RouteOptions(p, q, b)) -> Result(res, errors.AppError), map_fn: fn(wisp.Response) -> wisp.Response, ) -> Result(Router, wisp.Response) { use router <- result.try(router_res) let result = receive(router.req, route, handler) case result { Ok(response) -> Error(response |> map_fn) Error(Nil) -> Ok(router) } } pub fn unwrap(router_res: Result(Router, wisp.Response)) -> wisp.Response { case router_res { Ok(_) -> wisp.not_found() Error(response) -> response } } fn receive( req: wisp.Request, route: glitr.Route(p, q, b, res), handler: fn(glitr.RouteOptions(p, q, b)) -> Result(res, errors.AppError), ) -> Result(wisp.Response, Nil) { use <- bool.guard(req.method != route.method, Error(Nil)) use path <- handle_path(req, route) use query <- handle_query(req, route) use body <- handle_body(req, route) use <- handle_result let result = handler(glitr.RouteOptions(path, query, body)) result |> result.map(route.res_body_converter.encoder) |> result.map(json.to_string_builder) } fn handle_path( req: wisp.Request, route: glitr.Route(p, _, _, _), callback: fn(p) -> Result(wisp.Response, Nil), ) -> Result(wisp.Response, Nil) { let path_result = req |> wisp.path_segments |> route.path_converter.decoder case path_result { Ok(path) -> callback(path) Error(_) -> Error(Nil) } } fn handle_query( req: wisp.Request, route: glitr.Route(_, q, _, _), callback: fn(q) -> Result(wisp.Response, Nil), ) -> Result(wisp.Response, Nil) { let path_result = req |> wisp.get_query |> route.query_converter.decoder case path_result { Ok(path) -> callback(path) Error(_) -> Error(Nil) } } fn handle_body( req: wisp.Request, route: glitr.Route(_, _, b, _), callback: fn(b) -> wisp.Response, ) -> Result(wisp.Response, Nil) { let call_callback = fn(res) { case res { Ok(value) -> callback(value) Error(_) -> wisp.bad_request() |> wisp.string_body("Error while decoding body as JSON") } } let body = case route.has_body { False -> dynamic.from(Nil) |> route.req_body_converter.decoder |> call_callback True -> { use json <- wisp.require_json(req) json |> route.req_body_converter.decoder |> call_callback } } Ok(body) } fn handle_result( handler: fn() -> Result(string_builder.StringBuilder, errors.AppError), ) -> wisp.Response { let result = handler() case result { Ok(res) -> wisp.json_response(res, 200) Error(errors.DecoderError(msg)) -> wisp.json_response(string_builder.from_string(msg), 400) Error(errors.DBError(msg)) -> wisp.json_response(string_builder.from_string(msg), 500) Error(errors.InternalError(msg)) -> wisp.json_response(string_builder.from_string(msg), 500) } }