///|
/// Mars HTTP Framework
/// A Hono-inspired HTTP framework for MoonBit
pub(all) struct Mars {
router : @trie.TrieRouter
handlers : Array[Handler]
middlewares : Array[Handler]
}
///|
/// Create a new Mars application
pub fn Mars::new() -> Mars {
{ router: @trie.TrieRouter::new(), handlers: [], middlewares: [] }
}
///|
/// Add a middleware to the application
pub fn Mars::use_(self : Mars, handler : Handler) -> Mars {
self.middlewares.push(handler)
self
}
///|
/// Register a GET route
pub fn Mars::get(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::Get, path, handler)
}
///|
/// Register a POST route
pub fn Mars::post(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::Post, path, handler)
}
///|
/// Register a PUT route
pub fn Mars::put(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::Put, path, handler)
}
///|
/// Register a DELETE route
pub fn Mars::delete(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::Delete, path, handler)
}
///|
/// Register a PATCH route
pub fn Mars::patch(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::Patch, path, handler)
}
///|
/// Register a QUERY route (RFC 9110)
pub fn Mars::query(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::Query, path, handler)
}
///|
/// Register a route for all HTTP methods
pub fn Mars::all(self : Mars, path : String, handler : Handler) -> Mars {
self.add_route(@router.Method::All, path, handler)
}
///|
/// Internal: Add a route with method
fn Mars::add_route(
self : Mars,
meth : @router.Method,
path : String,
handler : Handler,
) -> Mars {
let @router.HandlerId(id) = self.router.add(meth, path)
// Ensure handlers array is large enough
while self.handlers.length() <= id {
// Placeholder handler
self.handlers.push(async fn(_ctx) { () })
}
self.handlers[id] = handler
self
}
///|
/// Convert @http.RequestMethod to @router.Method
fn request_method_to_method(meth : @http.RequestMethod) -> @router.Method {
match meth {
Get => @router.Method::Get
Post => @router.Method::Post
Put => @router.Method::Put
Delete => @router.Method::Delete
Patch => @router.Method::Patch
Head => @router.Method::Head
Options => @router.Method::Options
Connect => @router.Method::Connect
Trace => @router.Method::Trace
}
}
///|
/// Start the HTTP server
pub async fn Mars::serve(self : Mars, addr : @socket.Addr) -> Unit {
self.serve_with_env(addr, MapEnv::new())
}
///|
/// Start the HTTP server with environment bindings
pub async fn Mars::serve_with_env(
self : Mars,
addr : @socket.Addr,
env : MapEnv,
) -> Unit {
let server = @http.Server::new(addr)
server.run_forever(fn(request, body, conn) {
let meth = request_method_to_method(request.meth)
let result = self.router.match_(meth, request.path)
if result.handlers.is_empty() {
// No matching route - 404
conn.send_response(404, "Not Found")
conn.end_response()
} else {
// Get first matching handler
let (@router.HandlerId(id), params) = result.handlers[0]
let ctx = Context::with_env(request, params, conn, body, env)
// Run middlewares first
for middleware in self.middlewares {
if ctx.is_response_sent() {
break
}
let Handler(mw) = middleware
mw(ctx) catch {
e => {
println("Middleware error: \{e}")
break
}
}
}
// Then run the route handler
if not(ctx.is_response_sent()) && id < self.handlers.length() {
let Handler(h) = self.handlers[id]
h(ctx) catch {
e => {
println("Handler error: \{e}")
if not(ctx.is_response_sent()) {
conn.send_response(500, "Internal Server Error")
conn.end_response()
}
}
}
}
// If no response sent, send 404
if not(ctx.is_response_sent()) {
conn.send_response(404, "Not Found")
conn.end_response()
}
// Run background tasks after response is sent
if ctx.execution_ctx.pending_count() > 0 {
ctx.execution_ctx.run_tasks() catch {
e => println("Background tasks error: \{e}")
}
}
}
})
}
///|
/// Helper to create a Handler from an async function
pub fn handler(f : async (Context) -> Unit raise Error) -> Handler {
Handler(f)
}