pub struct Router { /* private fields */ }Expand description
A compiled, trie-based router mapping (method, path) to a handler.
Build one with Router::new, register routes with Router::add, and
look them up with Router::route. Inside an application you usually do not
touch the Router directly — the
RouteBuilder DSL in
AppBuilder::routing populates it for you.
Supported pattern syntax (full paths, leading /):
- static segments:
/users/list - named parameters:
/users/{id}(captured asid) - trailing wildcard:
/files/{path...}(captures the remaining path, slashes included; must be the last segment)
use churust_core::{boxed, Call, IntoHandler, Router, Match};
use http::Method;
let mut router = Router::new();
router.add(Method::GET, "/files/{path...}", boxed((|_c: Call| async { "" }).into_handler()));
match router.route(&Method::GET, "/files/a/b/c.txt", &probe("/files/a/b/c.txt")) {
Match::Found { params, .. } => assert_eq!(params.get("path").unwrap(), "a/b/c.txt"),
_ => panic!("expected wildcard match"),
}Implementations§
Source§impl Router
impl Router
Sourcepub fn new() -> Self
pub fn new() -> Self
Create an empty router. Equivalent to Router::default.
Sourcepub fn add(&mut self, method: Method, pattern: &str, handler: BoxHandler)
pub fn add(&mut self, method: Method, pattern: &str, handler: BoxHandler)
Insert handler for method at pattern (the full path, e.g.
/users/{id}).
Registering different methods at the same path is fine. Registering the
same (method, path) twice does not replace the earlier handler: an
unguarded duplicate is a mistake this router refuses to guess about, so
it panics. Several routes may share a (method, path) when guards tell
them apart — RouteBuilder::guard attaches one to the registration
just made — and those resolve first-match-wins in registration order, so
an unguarded route registered last is the fallback.
§Panics
Panics if a {name...} wildcard segment is not the final segment of the
pattern; if an unguarded route is already registered for this
(method, path); or if a {name} at this position was already
registered under a different name, since one trie node holds one
parameter name and the second route’s handler would look up a capture
that is not there.
use churust_core::{boxed, Call, IntoHandler, Router, Match};
use http::Method;
let mut router = Router::new();
router.add(Method::GET, "/ping", boxed((|_c: Call| async { "pong" }).into_handler()));
assert!(matches!(router.route(&Method::GET, "/ping", &probe("/ping")), Match::Found { .. }));Sourcepub fn route(&self, method: &Method, path: &str, call: &Call) -> Match
pub fn route(&self, method: &Method, path: &str, call: &Call) -> Match
Route path for method, returning a Match.
Matching prefers static segments over {param} captures, and falls back
to a {name...} wildcard at the deepest matchable ancestor. A path that
matches a node with no handler for method yields
Match::MethodNotAllowed; a path that matches no node yields
Match::NotFound.
use churust_core::{boxed, Call, IntoHandler, Router, Match};
use http::Method;
let mut router = Router::new();
router.add(Method::GET, "/", boxed((|_c: Call| async { "home" }).into_handler()));
assert!(matches!(router.route(&Method::GET, "/", &probe("/")), Match::Found { .. }));
assert!(matches!(router.route(&Method::GET, "/missing", &probe("/missing")), Match::NotFound));Sourcepub fn methods_for(&self, path: &str) -> Vec<Method>
pub fn methods_for(&self, path: &str) -> Vec<Method>
The methods registered for path, including any a trailing wildcard
would serve. Empty when the path matches no route at all.
Used by the dispatcher to build the Allow header for an OPTIONS
request that has no handler of its own.
use churust_core::{boxed, Call, IntoHandler, Router};
use http::Method;
let mut router = Router::new();
router.add(Method::GET, "/x", boxed((|_c: Call| async { "" }).into_handler()));
assert_eq!(router.methods_for("/x"), vec![Method::GET]);
assert!(router.methods_for("/nope").is_empty());Sourcepub fn all_methods(&self) -> Vec<Method>
pub fn all_methods(&self) -> Vec<Method>
Every method registered anywhere in this router, deduplicated.
Answers OPTIONS *, which RFC 9110 §9.3.7 defines as a question about
the server rather than about any one resource.
use churust_core::{boxed, Call, IntoHandler, Router};
use http::Method;
let mut router = Router::new();
router.add(Method::GET, "/a", boxed((|_c: Call| async { "" }).into_handler()));
router.add(Method::POST, "/b", boxed((|_c: Call| async { "" }).into_handler()));
let mut all = router.all_methods();
all.sort_by(|a, b| a.as_str().cmp(b.as_str()));
assert_eq!(all, vec![Method::GET, Method::POST]);Sourcepub fn routes(&self) -> &[(Method, String)]
pub fn routes(&self) -> &[(Method, String)]
Every registered route as (method, pattern), in registration order.
The patterns are exactly what was registered, {id} and {path...}
included, which is what makes this usable as the inventory an API
description is generated from. Routes that share a (method, path)
behind different guards appear once per registration, because each is a
separate route even though a client sees one URL.
use churust_core::{boxed, Call, IntoHandler, Router};
use http::Method;
let mut router = Router::new();
router.add(Method::GET, "/users/{id}", boxed((|_c: Call| async { "" }).into_handler()));
assert_eq!(router.routes(), &[(Method::GET, "/users/{id}".to_string())]);