// HTTP Framework for Lux // // Provides helpers for building web applications. // // Note: Due to current type system limitations, this module provides // helper functions rather than abstract types. Use the HttpServer effect // directly for the server loop. // ============================================================ // Response Builder Functions // ============================================================ // Create a 200 OK response fn httpOk(body: String): { status: Int, body: String } = { status: 200, body: body } // Create a 201 Created response fn httpCreated(body: String): { status: Int, body: String } = { status: 201, body: body } // Create a 204 No Content response fn httpNoContent(): { status: Int, body: String } = { status: 204, body: "" } // Create a 400 Bad Request response fn httpBadRequest(body: String): { status: Int, body: String } = { status: 400, body: body } // Create a 401 Unauthorized response fn httpUnauthorized(body: String): { status: Int, body: String } = { status: 401, body: body } // Create a 403 Forbidden response fn httpForbidden(body: String): { status: Int, body: String } = { status: 403, body: body } // Create a 404 Not Found response fn httpNotFound(body: String): { status: Int, body: String } = { status: 404, body: body } // Create a 500 Server Error response fn httpServerError(body: String): { status: Int, body: String } = { status: 500, body: body } // ============================================================ // Path Matching // ============================================================ // Check if a path matches a pattern with wildcards // Pattern "/users/:id" matches "/users/42" fn pathMatches(path: String, pattern: String): Bool = { let pathParts = String.split(path, "/") let patternParts = String.split(pattern, "/") if List.length(pathParts) != List.length(patternParts) then false else matchPathParts(pathParts, patternParts) } fn matchPathParts(pathParts: List, patternParts: List): Bool = { if List.length(pathParts) == 0 then true else { match List.head(pathParts) { None => true, Some(pathPart) => { match List.head(patternParts) { None => true, Some(patternPart) => { let isMatch = if String.startsWith(patternPart, ":") then true else pathPart == patternPart if isMatch then { let restPath = Option.getOrElse(List.tail(pathParts), []) let restPattern = Option.getOrElse(List.tail(patternParts), []) matchPathParts(restPath, restPattern) } else false } } } } } } // Extract a path segment by position (0-indexed, skipping leading empty segment) // For path "/users/42", getPathSegment(path, 1) returns Some("42") fn getPathSegment(path: String, index: Int): Option = { let parts = String.split(path, "/") List.get(parts, index + 1) } // ============================================================ // JSON Helpers // ============================================================ // Escape a string for JSON (handles quotes and backslashes) fn jsonEscape(s: String): String = { String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"") } // Create a JSON string field: "key": "value" fn jsonString(key: String, value: String): String = { "\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\"" } // Create a JSON number field: "key": 42 fn jsonNumber(key: String, value: Int): String = { "\"" + jsonEscape(key) + "\":" + toString(value) } // Create a JSON boolean field: "key": true fn jsonBool(key: String, value: Bool): String = { let boolStr = if value then "true" else "false" "\"" + jsonEscape(key) + "\":" + boolStr } // Wrap content in JSON object braces fn jsonObject(content: String): String = "{" + content + "}" // Wrap content in JSON array brackets fn jsonArray(content: String): String = "[" + content + "]" // Join multiple JSON fields/items with commas fn jsonJoin(items: List): String = String.join(items, ",") // Create a JSON error object: {"error": "message"} fn jsonErrorMsg(message: String): String = jsonObject(jsonString("error", message)) // Create a JSON message object: {"message": "text"} fn jsonMessage(text: String): String = jsonObject(jsonString("message", text)) // ============================================================ // Usage Example (copy into your file) // ============================================================ // // fn router(method: String, path: String, body: String): { status: Int, body: String } = { // if method == "GET" && path == "/" then httpOk("Welcome!") // else if method == "GET" && pathMatches(path, "/users/:id") then { // match getPathSegment(path, 1) { // Some(id) => httpOk(jsonObject(jsonString("id", id))), // None => httpNotFound(jsonErrorMsg("User not found")) // } // } // else httpNotFound(jsonErrorMsg("Not found")) // } // // fn main(): Unit with {Console, HttpServer} = { // HttpServer.listen(8080) // Console.print("Server running on port 8080") // serveLoop(5) // Handle 5 requests // } // // fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = { // if remaining <= 0 then HttpServer.stop() // else { // let req = HttpServer.accept() // let resp = router(req.method, req.path, req.body) // HttpServer.respond(resp.status, resp.body) // serveLoop(remaining - 1) // } // }