pub struct Router { /* private fields */ }Expand description
The Actus router. Dispatches requests to controllers by longest-prefix match over the route tree at arbitrary depth.
Implementations§
Source§impl Router
impl Router
Sourcepub fn match_controller(&self, path_parts: &[String]) -> Option<RouteMatch>
pub fn match_controller(&self, path_parts: &[String]) -> Option<RouteMatch>
Find which controller a path would hit and the leftover-segments
action, without invoking the controller. Returns None if no
controller matches (→ 404).
The verb-level resolution (which routes! line the action will
dispatch to, or 405 if none match the verb) happens inside the
controller’s actus_dispatch; this method only does the
path-tree walk.
Sourcepub async fn route(&self, path_parts: &[String], params: Params) -> Reply
pub async fn route(&self, path_parts: &[String], params: Params) -> Reply
Routes a request to the appropriate controller and action: thin
wrapper around Router::match_controller that also invokes
actus_dispatch. Kept for callers (mostly tests) that don’t need
to inspect the matched controller before dispatch — the server
uses the two-step shape so it can buffer the body with the right
cap between match and dispatch.
Sourcepub fn routes(&self) -> Vec<(String, RouteDef)>
pub fn routes(&self) -> Vec<(String, RouteDef)>
Walk the route tree and return every (mount_path, RouteDef) pair.
mount_path is the slash-joined path segments where the controller
was mounted (no leading or trailing slash) — "" for a root mount,
"api/users" for app_routes!{ "api/users" => UserController, ... },
etc. Each controller contributes one entry per RouteDef it
describes via Controller::actus_describe_routes.
Used by introspection tools (OpenAPI doc generators, route audit
scripts). Order matches a DFS over the route tree; within a single
controller, routes are emitted in the order
Controller::actus_describe_routes returns them (which is the order
they were declared in the routes! block).
Sourcepub fn rate_limit_classes(&self) -> Vec<RateLimitClass>
pub fn rate_limit_classes(&self) -> Vec<RateLimitClass>
Walk the route tree and return (mount_path, class) for every mounted
controller that declared a rate-limit class via
#[controller(rate_limit = "…")]. Controllers with no class are
skipped. mount_path follows the same convention as Router::routes
("" for a root mount); order is a deterministic DFS (children sorted),
so diagnostics and tests get stable output.
This exposes the declared half of the rate-limit picture — the half
only the router knows. An application’s rate-limit middleware holds the
other half (the classes it has a policy for), so main() can diff the
two at startup and assert every declared class is covered. That turns a
typo’d class ("ath" for "auth") into a boot failure instead of a
silently-unlimited controller. One tree walk; no per-request cost.
⚠️ Emptiness asymmetry. This method catches a misspelling and is
blind to an omission: a controller that declared no class produces no
row here, so nothing can notice its absence. For rate limiting that is
usually fine (an unlimited controller is usually intended). When the
omission itself is the failure you’re checking for — route-family
coverage — use Router::mounts, which emits a row for every mounted
controller, declared or not. (This method keeps its historical shape;
mounts() is the absence-inclusive inventory.)
Sourcepub fn mounts(&self) -> Vec<Mount>
pub fn mounts(&self) -> Vec<Mount>
Walk the route tree and return one Mount row per mounted
controller — the per-mount inventory: mount path, controller name,
declared caller expectation (floor), prepare hook presence,
rate-limit class, and body cap.
Absence is a row, not a skip. A controller that declared nothing
appears with expects: None, which is what makes a route-family
coverage check possible: the check’s job is precisely to catch the
controller that silently declared nothing under a prefix whose other
controllers all did. (Contrast Router::rate_limit_classes, which
emits only declaring controllers.)
Order is a deterministic DFS (children sorted), matching
Router::routes; mount follows the same convention ("" for a
root mount). One tree walk; no per-request cost. Typical use: a
startup coverage check in main(), a route dump, or building a
mount → floor map for a declaration-keyed gate — see the README’s
“Route families” section.