122 lines
4.7 KiB
Plaintext
122 lines
4.7 KiB
Plaintext
fn httpOk(body: String): { status: Int, body: String } = { status: 200, body: body }
|
|
|
|
fn httpCreated(body: String): { status: Int, body: String } = { status: 201, body: body }
|
|
|
|
fn httpNotFound(body: String): { status: Int, body: String } = { status: 404, body: body }
|
|
|
|
fn httpBadRequest(body: String): { status: Int, body: String } = { status: 400, body: body }
|
|
|
|
fn jsonEscape(s: String): String = String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"")
|
|
|
|
fn jsonStr(key: String, value: String): String = "\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\""
|
|
|
|
fn jsonNum(key: String, value: Int): String = "\"" + jsonEscape(key) + "\":" + toString(value)
|
|
|
|
fn jsonObj(content: String): String = toString(" + content + ")
|
|
|
|
fn jsonArr(content: String): String = "[" + content + "]"
|
|
|
|
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)
|
|
}
|
|
|
|
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
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn getPathSegment(path: String, index: Int): Option<String> = {
|
|
let parts = String.split(path, "/")
|
|
List.get(parts, index + 1)
|
|
}
|
|
|
|
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 listUsersHandler(): { status: Int, body: String } = {
|
|
let user1 = jsonObj(jsonNum("id", 1) + "," + jsonStr("name", "Alice"))
|
|
let user2 = jsonObj(jsonNum("id", 2) + "," + jsonStr("name", "Bob"))
|
|
httpOk(jsonArr(user1 + "," + user2))
|
|
}
|
|
|
|
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")),
|
|
}
|
|
}
|
|
|
|
fn createUserHandler(body: String): { status: Int, body: String } = {
|
|
let newUser = jsonObj(jsonNum("id", 3) + "," + jsonStr("name", "New User"))
|
|
httpCreated(newUser)
|
|
}
|
|
|
|
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))
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
fn main(): Unit with {Console, HttpServer} = {
|
|
let port = 8080
|
|
let maxRequests = 10
|
|
Console.print("========================================")
|
|
Console.print(" Lux HTTP API Demo")
|
|
Console.print("========================================")
|
|
Console.print("")
|
|
Console.print("Endpoints:")
|
|
Console.print(" GET / - API info")
|
|
Console.print(" GET /health - Health check")
|
|
Console.print(" GET /users - List users")
|
|
Console.print(" GET /users/:id - Get user by ID")
|
|
Console.print(" POST /users - Create user")
|
|
Console.print("")
|
|
Console.print("Try:")
|
|
Console.print(" curl http://localhost:8080/")
|
|
Console.print(" curl http://localhost:8080/users")
|
|
Console.print(" curl http://localhost:8080/users/42")
|
|
Console.print(" curl -X POST http://localhost:8080/users")
|
|
Console.print("")
|
|
Console.print("Starting server on port " + toString(port) + "...")
|
|
HttpServer.listen(port)
|
|
Console.print("Server listening!")
|
|
serveLoop(maxRequests)
|
|
}
|
|
|
|
let output = run main() with {}
|