From f7758ffea152fd910b9da1fc0b8ef3ed636fdfe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Mon, 5 Sep 2022 21:36:04 +0200 Subject: [PATCH] Expose the matched route as an extension on the request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Johannes Löthberg --- src/lib.rs | 2 +- src/route.rs | 23 +++++++++++++++++++++++ src/router.rs | 5 +++++ src/server.rs | 25 +++++++++++++++++++++---- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b9e739d8..e31b4d49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,7 @@ pub use redirect::Redirect; pub use request::Request; pub use response::Response; pub use response_builder::ResponseBuilder; -pub use route::Route; +pub use route::{MatchedRoute, Route}; pub use server::Server; pub use http_types::{self as http, Body, Error, Status, StatusCode}; diff --git a/src/route.rs b/src/route.rs index e52889fd..823b1c61 100644 --- a/src/route.rs +++ b/src/route.rs @@ -9,6 +9,29 @@ use crate::{router::Router, Endpoint, Middleware}; use kv_log_macro::trace; +/// Extension struct for exposing the matched route. +/// +/// # Examples +/// +/// ``` +/// # use tide::{MatchedRoute, Request}; +/// let mut app = tide::Server::new(); +/// app.at("/route").get(|req: Request<()>| async move { +/// let route: &str = req.ext::().unwrap(); +/// Ok(route.to_string()) +/// }); +/// ``` +#[derive(Debug)] +pub struct MatchedRoute(pub String); + +impl std::ops::Deref for MatchedRoute { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + /// A handle to a route. /// /// All HTTP requests are made against resources. After using [`Server::at`] (or diff --git a/src/router.rs b/src/router.rs index 70b1ab35..288f80f2 100644 --- a/src/router.rs +++ b/src/router.rs @@ -27,6 +27,7 @@ impl std::fmt::Debug for Router { pub(crate) struct Selection<'a, State> { pub(crate) endpoint: &'a DynEndpoint, pub(crate) params: Captures<'static, 'static>, + pub(crate) matched_route: Option, } impl Router { @@ -63,11 +64,13 @@ impl Router { Selection { endpoint: m.handler(), params: m.captures().into_owned(), + matched_route: Some(m.route().to_string()), } } else if let Some(m) = self.all_method_router.best_match(path) { Selection { endpoint: m.handler(), params: m.captures().into_owned(), + matched_route: Some(m.route().to_string()), } } else if method == http_types::Method::Head { // If it is a HTTP HEAD request then check if there is a callback in the endpoints map @@ -85,11 +88,13 @@ impl Router { Selection { endpoint: &method_not_allowed, params: Captures::default(), + matched_route: None, } } else { Selection { endpoint: ¬_found_endpoint, params: Captures::default(), + matched_route: None, } } } diff --git a/src/server.rs b/src/server.rs index 3b09fc40..faf4c05b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,6 +8,7 @@ use kv_log_macro::{info, trace}; use crate::cookies; use crate::listener::{Listener, ToListener}; use crate::middleware::{Middleware, Next}; +use crate::route::MatchedRoute; use crate::router::{Router, Selection}; use crate::{Endpoint, Request, Route}; @@ -287,9 +288,17 @@ where } = self.clone(); let method = req.method().to_owned(); - let Selection { endpoint, params } = router.route(req.url().path(), method); + let Selection { + endpoint, + params, + matched_route, + } = router.route(req.url().path(), method); let route_params = vec![params]; - let req = Request::new(state, req, route_params); + let mut req = Request::new(state, req, route_params); + + if let Some(route) = matched_route { + req.set_ext(MatchedRoute(route)); + } let next = Next { endpoint, @@ -349,9 +358,17 @@ impl