///|
/// The request method a route binds to: the nine standard ones and any other
/// token, from moonhttp.
pub using @method {type Method}
///|
/// A route handler (← a FastAPI path operation): the request context in, a response out. It runs
/// async, so it can await a database or another service; a plain closure is one too, and a named
/// function that is not async is registered as `ctx => f(ctx)`. It may `raise` an
/// `HttpException` (or any error) instead of returning — the app maps it to a response through
/// the registered exception handlers.
pub type ApiHandler = async (@ctx.Context) -> @moonasgi.Response
///|
/// A streaming route's handler (← a FastAPI path operation returning a `StreamingResponse`): the
/// response it returns writes its body while it is sent.
pub type StreamHandler = async (@ctx.Context) -> @status.Stream
///|
/// A middleware on a route or on a router node (← Salvo's hoops, Starlette's route `middleware`):
/// the request context and the rest of the route in, the reply out. It runs inside the app's own
/// middleware and outside the route's security, validation and dependencies, so it can answer
/// for the route — refuse, redirect, serve from a cache — or change what the route produced, and
/// it can hand `next` a context of its own.
pub type Hoop = async (@ctx.Context, async (@ctx.Context) -> Reply) -> Reply
///|
/// What a route produced: a response with its body in hand, or one that writes its body as it
/// goes.
pub(all) enum Reply {
Buffered(@moonasgi.Response)
Streamed(@status.Stream)
}
///|
/// One registered route, the record an app keeps for each operation — what
/// FastAPI calls `APIRoute`. Public so a program can list and inspect an app's
/// routes; building one by hand is possible but the `App` methods are the way.
///
/// `security` is the per-operation requirements (emitted as OpenAPI `security` and enforced
/// before the handler when their scheme has an enforcer).
pub(all) struct Route {
verb : Method
path : String
// `path` read once at registration, so a request is matched without reading
// the template again.
pattern : Pattern
run : async (@ctx.Context) -> Reply
summary : String
description : String
tags : Array[String]
deprecated : Bool
operation_id : String
// The status the success response is documented under. Documentation only: the
// handler builds its own response, and rewriting the status it chose would make
// the app disagree with itself rather than with the spec.
status_code : Int32?
// Documented alongside whatever the endpoint descriptor produced, and written
// after it, so the status a route names here is the one that stands.
responses : Array[@endpoint.ResponseSpec]
// The handle `App::url_for` looks the route up by. Empty means unnamed, and an
// unnamed route is not addressable.
name : String
// Merged into the operation object last, so it can override anything moonapi
// generated (← FastAPI's `openapi_extra`).
openapi_extra : Json?
endpoint : @endpoint.Endpoint?
security : Array[@security.SecurityRequirement]
// Provider keys resolved through the app's container before the handler runs,
// and torn down after it (← FastAPI's `dependencies=[Depends(...)]`, whose
// values the handler never sees).
dependencies : Array[String]
// The requests the operation makes back to its caller (← FastAPI's
// `callbacks`): documented under the operation, never served. Each one's
// path is a runtime expression such as `{$request.body#/url}`.
callbacks : Array[Route]
// Outermost first: the hoops of the include calls and routers that led here,
// then the route's own.
hoops : Array[Hoop]
// FastAPI's `include_in_schema`: a route that serves the documentation itself has
// no business appearing in the document it serves.
include_in_schema : Bool
// Whether the declared `endpoint` is checked before the handler runs. On by
// default: a route that advertises constraints in its documentation and does not
// apply them is worse than one that declares nothing.
validate : Bool
}
///|
/// One registered WebSocket route (← Starlette's `WebSocketRoute`). `security` and
/// `dependencies` are settled before the handler is called, so a connection they turn away is
/// declined at the handshake rather than accepted and dropped.
pub(all) struct WsRoute {
path : String
pattern : Pattern
handler : @socket.WsHandler
// The handle `App::url_for` looks the route up by; empty is unnamed.
name : String
security : Array[@security.SecurityRequirement]
dependencies : Array[String]
}
///|
/// Split a path into its non-empty segments.
pub fn segments(path : String) -> Array[String] {
let out : Array[String] = []
let n = path.length()
let mut start = 0
for i = 0; i <= n; i = i + 1 {
if i == n || path[i] == '/' {
if i > start {
out.push(path[start:i].to_owned())
}
start = i + 1
}
}
out
}