style: auto-format example files with lux fmt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,85 +1,48 @@
|
||||
// HTTP API Example
|
||||
//
|
||||
// A complete REST API demonstrating:
|
||||
// - Route matching with path parameters
|
||||
// - Response builders
|
||||
// - JSON construction
|
||||
//
|
||||
// Run with: lux examples/http_api.lux
|
||||
// Test with:
|
||||
// curl http://localhost:8080/
|
||||
// curl http://localhost:8080/users
|
||||
// curl http://localhost:8080/users/42
|
||||
fn httpOk(body: String): { status: Int, body: String } = { status: 200, body: body }
|
||||
|
||||
// ============================================================
|
||||
// Response Helpers
|
||||
// ============================================================
|
||||
fn httpCreated(body: String): { status: Int, body: String } = { status: 201, body: body }
|
||||
|
||||
fn httpOk(body: String): { status: Int, body: String } =
|
||||
{ status: 200, body: body }
|
||||
fn httpNotFound(body: String): { status: Int, body: String } = { status: 404, body: body }
|
||||
|
||||
fn httpCreated(body: String): { status: Int, body: String } =
|
||||
{ status: 201, body: body }
|
||||
fn httpBadRequest(body: String): { status: Int, body: String } = { status: 400, body: body }
|
||||
|
||||
fn httpNotFound(body: String): { status: Int, body: String } =
|
||||
{ status: 404, body: body }
|
||||
fn jsonEscape(s: String): String = String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"")
|
||||
|
||||
fn httpBadRequest(body: String): { status: Int, body: String } =
|
||||
{ status: 400, body: body }
|
||||
fn jsonStr(key: String, value: String): String = "\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\""
|
||||
|
||||
// ============================================================
|
||||
// JSON Helpers
|
||||
// ============================================================
|
||||
fn jsonNum(key: String, value: Int): String = "\"" + jsonEscape(key) + "\":" + toString(value)
|
||||
|
||||
fn jsonEscape(s: String): String =
|
||||
String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"")
|
||||
fn jsonObj(content: String): String = toString(" + content + ")
|
||||
|
||||
fn jsonStr(key: String, value: String): String =
|
||||
"\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\""
|
||||
fn jsonArr(content: String): String = "[" + content + "]"
|
||||
|
||||
fn jsonNum(key: String, value: Int): String =
|
||||
"\"" + jsonEscape(key) + "\":" + toString(value)
|
||||
|
||||
fn jsonObj(content: String): String =
|
||||
"{" + content + "}"
|
||||
|
||||
fn jsonArr(content: String): String =
|
||||
"[" + content + "]"
|
||||
|
||||
fn jsonError(message: String): String =
|
||||
jsonObj(jsonStr("error", message))
|
||||
|
||||
// ============================================================
|
||||
// Path Matching
|
||||
// ============================================================
|
||||
fn jsonError(message: String): String = jsonObj(jsonStr("error", message))
|
||||
|
||||
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 matchParts(pathParts, patternParts)
|
||||
if List.length(pathParts) != List.length(patternParts) then false else matchParts(pathParts, patternParts)
|
||||
}
|
||||
|
||||
fn matchParts(pathParts: List<String>, patternParts: List<String>): 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), [])
|
||||
matchParts(restPath, restPattern)
|
||||
} else false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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), [])
|
||||
matchParts(restPath, restPattern)
|
||||
} else false
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn getPathSegment(path: String, index: Int): Option<String> = {
|
||||
@@ -87,15 +50,9 @@ fn getPathSegment(path: String, index: Int): Option<String> = {
|
||||
List.get(parts, index + 1)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Handlers
|
||||
// ============================================================
|
||||
fn indexHandler(): { status: Int, body: String } = httpOk(jsonObj(jsonStr("message", "Welcome to Lux HTTP API")))
|
||||
|
||||
fn indexHandler(): { status: Int, body: String } =
|
||||
httpOk(jsonObj(jsonStr("message", "Welcome to Lux HTTP API")))
|
||||
|
||||
fn healthHandler(): { status: Int, body: String } =
|
||||
httpOk(jsonObj(jsonStr("status", "healthy")))
|
||||
fn healthHandler(): { status: Int, body: String } = httpOk(jsonObj(jsonStr("status", "healthy")))
|
||||
|
||||
fn listUsersHandler(): { status: Int, body: String } = {
|
||||
let user1 = jsonObj(jsonNum("id", 1) + "," + jsonStr("name", "Alice"))
|
||||
@@ -105,12 +62,12 @@ fn listUsersHandler(): { status: Int, body: String } = {
|
||||
|
||||
fn getUserHandler(path: String): { status: Int, body: String } = {
|
||||
match getPathSegment(path, 1) {
|
||||
Some(id) => {
|
||||
let body = jsonObj(jsonStr("id", id) + "," + jsonStr("name", "User " + id))
|
||||
httpOk(body)
|
||||
},
|
||||
None => httpNotFound(jsonError("User not found"))
|
||||
}
|
||||
Some(id) => {
|
||||
let body = jsonObj(jsonStr("id", id) + "," + jsonStr("name", "User " + id))
|
||||
httpOk(body)
|
||||
},
|
||||
None => httpNotFound(jsonError("User not found")),
|
||||
}
|
||||
}
|
||||
|
||||
fn createUserHandler(body: String): { status: Int, body: String } = {
|
||||
@@ -118,34 +75,21 @@ fn createUserHandler(body: String): { status: Int, body: String } = {
|
||||
httpCreated(newUser)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Router
|
||||
// ============================================================
|
||||
|
||||
fn router(method: String, path: String, body: String): { status: Int, body: String } = {
|
||||
if method == "GET" && path == "/" then indexHandler()
|
||||
else if method == "GET" && path == "/health" then healthHandler()
|
||||
else if method == "GET" && path == "/users" then listUsersHandler()
|
||||
else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path)
|
||||
else if method == "POST" && path == "/users" then createUserHandler(body)
|
||||
else httpNotFound(jsonError("Not found: " + path))
|
||||
if method == "GET" && path == "/" then indexHandler() else if method == "GET" && path == "/health" then healthHandler() else if method == "GET" && path == "/users" then listUsersHandler() else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path) else if method == "POST" && path == "/users" then createUserHandler(body) else httpNotFound(jsonError("Not found: " + path))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Server
|
||||
// ============================================================
|
||||
|
||||
fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = {
|
||||
if remaining <= 0 then {
|
||||
Console.print("Max requests reached, stopping server.")
|
||||
HttpServer.stop()
|
||||
} else {
|
||||
let req = HttpServer.accept()
|
||||
Console.print(req.method + " " + req.path)
|
||||
let resp = router(req.method, req.path, req.body)
|
||||
HttpServer.respond(resp.status, resp.body)
|
||||
serveLoop(remaining - 1)
|
||||
}
|
||||
Console.print("Max requests reached, stopping server.")
|
||||
HttpServer.stop()
|
||||
} else {
|
||||
let req = HttpServer.accept()
|
||||
Console.print(req.method + " " + req.path)
|
||||
let resp = router(req.method, req.path, req.body)
|
||||
HttpServer.respond(resp.status, resp.body)
|
||||
serveLoop(remaining - 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn main(): Unit with {Console, HttpServer} = {
|
||||
|
||||
Reference in New Issue
Block a user