Skip to content

Commit 81d566b

Browse files
committed
try using routefinder
1 parent d81eabc commit 81d566b

File tree

5 files changed

+90
-103
lines changed

5 files changed

+90
-103
lines changed

Cargo.toml

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name = "tide"
33
version = "0.16.0"
44
description = "A minimal and pragmatic Rust web application framework built for rapid development"
55
authors = [
6-
"Aaron Turon <aturon@mozilla.com>",
7-
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
8-
"Wonwoo Choi <chwo9843@gmail.com>",
6+
"Aaron Turon <aturon@mozilla.com>",
7+
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
8+
"Wonwoo Choi <chwo9843@gmail.com>",
99
]
1010
documentation = "https://docs.rs/tide"
1111
keywords = ["tide", "http", "web", "framework", "async"]
@@ -34,7 +34,7 @@ unstable = []
3434

3535
[dependencies]
3636
async-h1 = { version = "2.3.0", optional = true }
37-
async-session = { version = "2.0.1", optional = true }
37+
async-session = { version = "2.0.1", optional = true }
3838
async-sse = "4.0.1"
3939
async-std = { version = "1.6.5", features = ["unstable"] }
4040
async-trait = "0.1.41"
@@ -45,9 +45,9 @@ http-types = { version = "2.11.0", default-features = false, features = ["fs"] }
4545
kv-log-macro = "1.0.7"
4646
log = { version = "0.4.13", features = ["kv_unstable_std"] }
4747
pin-project-lite = "0.2.0"
48-
route-recognizer = "0.2.0"
4948
serde = "1.0.117"
5049
serde_json = "1.0.59"
50+
routefinder = "0.1.1"
5151

5252
[dev-dependencies]
5353
async-std = { version = "1.6.5", features = ["unstable", "attributes"] }

src/request.rs

+36-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use async_std::io::{self, prelude::*};
22
use async_std::task::{Context, Poll};
3-
use route_recognizer::Params;
3+
use routefinder::Captures;
44

55
use std::ops::Index;
66
use std::pin::Pin;
@@ -27,13 +27,13 @@ pin_project_lite::pin_project! {
2727
pub(crate) state: State,
2828
#[pin]
2929
pub(crate) req: http::Request,
30-
pub(crate) route_params: Vec<Params>,
30+
pub(crate) route_params: Vec<Captures>,
3131
}
3232
}
3333

3434
impl<State> Request<State> {
3535
/// Create a new `Request`.
36-
pub(crate) fn new(state: State, req: http_types::Request, route_params: Vec<Params>) -> Self {
36+
pub(crate) fn new(state: State, req: http_types::Request, route_params: Vec<Captures>) -> Self {
3737
Self {
3838
state,
3939
req,
@@ -266,8 +266,7 @@ impl<State> Request<State> {
266266
///
267267
/// Returns the parameter as a `&str`, borrowed from this `Request`.
268268
///
269-
/// The name should *not* include the leading `:` or the trailing `*` (if
270-
/// any).
269+
/// The name should *not* include the leading `:`.
271270
///
272271
/// # Errors
273272
///
@@ -297,10 +296,40 @@ impl<State> Request<State> {
297296
self.route_params
298297
.iter()
299298
.rev()
300-
.find_map(|params| params.find(key))
299+
.find_map(|captures| captures.get(key))
301300
.ok_or_else(|| format_err!("Param \"{}\" not found", key.to_string()))
302301
}
303302

303+
/// Fetch the wildcard from the route, if it exists
304+
///
305+
/// Returns the parameter as a `&str`, borrowed from this `Request`.
306+
///
307+
/// # Examples
308+
///
309+
/// ```no_run
310+
/// # use async_std::task::block_on;
311+
/// # fn main() -> Result<(), std::io::Error> { block_on(async {
312+
/// #
313+
/// use tide::{Request, Result};
314+
///
315+
/// async fn greet(req: Request<()>) -> Result<String> {
316+
/// let name = req.wildcard().unwrap_or("world");
317+
/// Ok(format!("Hello, {}!", name))
318+
/// }
319+
///
320+
/// let mut app = tide::new();
321+
/// app.at("/hello/*").get(greet);
322+
/// app.listen("127.0.0.1:8080").await?;
323+
/// #
324+
/// # Ok(()) })}
325+
/// ```
326+
pub fn wildcard(&self) -> Option<&str> {
327+
self.route_params
328+
.iter()
329+
.rev()
330+
.find_map(|captures| captures.wildcard())
331+
}
332+
304333
/// Parse the URL query component into a struct, using [serde_qs](https://docs.rs/serde_qs). To
305334
/// get the entire query as an unparsed string, use `request.url().query()`.
306335
///
@@ -565,7 +594,7 @@ impl<State> From<Request<State>> for http::Request {
565594

566595
impl<State: Default> From<http_types::Request> for Request<State> {
567596
fn from(request: http_types::Request) -> Request<State> {
568-
Request::new(State::default(), request, Vec::<Params>::new())
597+
Request::new(State::default(), request, vec![])
569598
}
570599
}
571600

@@ -635,9 +664,3 @@ impl<State> Index<&str> for Request<State> {
635664
&self.req[name]
636665
}
637666
}
638-
639-
pub(crate) fn rest(route_params: &[Params]) -> Option<&str> {
640-
route_params
641-
.last()
642-
.and_then(|params| params.find("--tide-path-rest"))
643-
}

src/route.rs

+8-14
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> {
153153
pub fn method(&mut self, method: http_types::Method, ep: impl Endpoint<State>) -> &mut Self {
154154
if self.prefix {
155155
let ep = StripPrefixEndpoint::new(ep);
156-
157-
self.router.add(
158-
&self.path,
159-
method,
160-
MiddlewareEndpoint::wrap_with_middleware(ep.clone(), &self.middleware),
161-
);
162-
let wildcard = self.at("*--tide-path-rest");
156+
let wildcard = self.at("*");
163157
wildcard.router.add(
164158
&wildcard.path,
165159
method,
@@ -181,12 +175,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> {
181175
pub fn all(&mut self, ep: impl Endpoint<State>) -> &mut Self {
182176
if self.prefix {
183177
let ep = StripPrefixEndpoint::new(ep);
184-
185-
self.router.add_all(
186-
&self.path,
187-
MiddlewareEndpoint::wrap_with_middleware(ep.clone(), &self.middleware),
188-
);
189-
let wildcard = self.at("*--tide-path-rest");
178+
let wildcard = self.at("*");
190179
wildcard.router.add_all(
191180
&wildcard.path,
192181
MiddlewareEndpoint::wrap_with_middleware(ep, &wildcard.middleware),
@@ -283,7 +272,12 @@ where
283272
route_params,
284273
} = req;
285274

286-
let rest = crate::request::rest(&route_params).unwrap_or("");
275+
let rest = route_params
276+
.iter()
277+
.rev()
278+
.find_map(|captures| captures.wildcard())
279+
.unwrap_or_default();
280+
287281
req.url_mut().set_path(&rest);
288282

289283
self.0

src/router.rs

+23-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use route_recognizer::{Match, Params, Router as MethodRouter};
1+
use routefinder::{Captures, Router as MethodRouter};
22
use std::collections::HashMap;
33

44
use crate::endpoint::DynEndpoint;
@@ -14,11 +14,19 @@ pub(crate) struct Router<State> {
1414
all_method_router: MethodRouter<Box<DynEndpoint<State>>>,
1515
}
1616

17+
impl<State> std::fmt::Debug for Router<State> {
18+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19+
f.debug_struct("Router")
20+
.field("method_map", &self.method_map)
21+
.field("all_method_router", &self.all_method_router)
22+
.finish()
23+
}
24+
}
25+
1726
/// The result of routing a URL
18-
#[allow(missing_debug_implementations)]
1927
pub(crate) struct Selection<'a, State> {
2028
pub(crate) endpoint: &'a DynEndpoint<State>,
21-
pub(crate) params: Params,
29+
pub(crate) params: Captures,
2230
}
2331

2432
impl<State: Clone + Send + Sync + 'static> Router<State> {
@@ -39,26 +47,27 @@ impl<State: Clone + Send + Sync + 'static> Router<State> {
3947
.entry(method)
4048
.or_insert_with(MethodRouter::new)
4149
.add(path, ep)
50+
.unwrap()
4251
}
4352

4453
pub(crate) fn add_all(&mut self, path: &str, ep: Box<DynEndpoint<State>>) {
45-
self.all_method_router.add(path, ep)
54+
self.all_method_router.add(path, ep).unwrap()
4655
}
4756

4857
pub(crate) fn route(&self, path: &str, method: http_types::Method) -> Selection<'_, State> {
49-
if let Some(Match { handler, params }) = self
58+
if let Some(m) = self
5059
.method_map
5160
.get(&method)
52-
.and_then(|r| r.recognize(path).ok())
61+
.and_then(|r| r.best_match(path))
5362
{
5463
Selection {
55-
endpoint: &**handler,
56-
params,
64+
endpoint: m.handler(),
65+
params: m.captures(),
5766
}
58-
} else if let Ok(Match { handler, params }) = self.all_method_router.recognize(path) {
67+
} else if let Some(m) = self.all_method_router.best_match(path) {
5968
Selection {
60-
endpoint: &**handler,
61-
params,
69+
endpoint: m.handler(),
70+
params: m.captures(),
6271
}
6372
} else if method == http_types::Method::Head {
6473
// If it is a HTTP HEAD request then check if there is a callback in the endpoints map
@@ -69,18 +78,18 @@ impl<State: Clone + Send + Sync + 'static> Router<State> {
6978
.method_map
7079
.iter()
7180
.filter(|(k, _)| **k != method)
72-
.any(|(_, r)| r.recognize(path).is_ok())
81+
.any(|(_, r)| r.best_match(path).is_some())
7382
{
7483
// If this `path` can be handled by a callback registered with a different HTTP method
7584
// should return 405 Method Not Allowed
7685
Selection {
7786
endpoint: &method_not_allowed,
78-
params: Params::new(),
87+
params: Captures::default(),
7988
}
8089
} else {
8190
Selection {
8291
endpoint: &not_found_endpoint,
83-
params: Params::new(),
92+
params: Captures::default(),
8493
}
8594
}
8695
}

tests/wildcard.rs

+18-57
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@ async fn add_two(req: Request<()>) -> Result<String, tide::Error> {
2222
Ok((one + two).to_string())
2323
}
2424

25-
async fn echo_path(req: Request<()>) -> Result<String, tide::Error> {
26-
match req.param("path") {
25+
async fn echo_param(req: Request<()>) -> tide::Result<tide::Response> {
26+
match req.param("param") {
2727
Ok(path) => Ok(path.into()),
28-
Err(mut err) => {
29-
err.set_status(StatusCode::BadRequest);
30-
Err(err)
31-
}
28+
Err(_) => Ok(StatusCode::NotFound.into()),
29+
}
30+
}
31+
32+
async fn echo_wildcard(req: Request<()>) -> tide::Result<tide::Response> {
33+
match req.wildcard() {
34+
Some(path) => Ok(path.into()),
35+
None => Ok(StatusCode::NotFound.into()),
3236
}
3337
}
3438

3539
#[async_std::test]
36-
async fn wildcard() -> tide::Result<()> {
40+
async fn param() -> tide::Result<()> {
3741
let mut app = tide::Server::new();
3842
app.at("/add_one/:num").get(add_one);
3943
assert_eq!(app.get("/add_one/3").recv_string().await?, "4");
@@ -61,20 +65,21 @@ async fn not_found_error() -> tide::Result<()> {
6165
}
6266

6367
#[async_std::test]
64-
async fn wild_path() -> tide::Result<()> {
68+
async fn wildcard() -> tide::Result<()> {
6569
let mut app = tide::new();
66-
app.at("/echo/*path").get(echo_path);
70+
app.at("/echo/*").get(echo_wildcard);
6771
assert_eq!(app.get("/echo/some_path").recv_string().await?, "some_path");
6872
assert_eq!(
6973
app.get("/echo/multi/segment/path").recv_string().await?,
7074
"multi/segment/path"
7175
);
72-
assert_eq!(app.get("/echo/").await?.status(), StatusCode::NotFound);
76+
assert_eq!(app.get("/echo/").await?.status(), StatusCode::Ok);
77+
assert_eq!(app.get("/echo").await?.status(), StatusCode::Ok);
7378
Ok(())
7479
}
7580

7681
#[async_std::test]
77-
async fn multi_wildcard() -> tide::Result<()> {
82+
async fn multi_param() -> tide::Result<()> {
7883
let mut app = tide::new();
7984
app.at("/add_two/:one/:two/").get(add_two);
8085
assert_eq!(app.get("/add_two/1/2/").recv_string().await?, "3");
@@ -84,9 +89,9 @@ async fn multi_wildcard() -> tide::Result<()> {
8489
}
8590

8691
#[async_std::test]
87-
async fn wild_last_segment() -> tide::Result<()> {
92+
async fn wildcard_last_segment() -> tide::Result<()> {
8893
let mut app = tide::new();
89-
app.at("/echo/:path/*").get(echo_path);
94+
app.at("/echo/:param/*").get(echo_param);
9095
assert_eq!(app.get("/echo/one/two").recv_string().await?, "one");
9196
assert_eq!(
9297
app.get("/echo/one/two/three/four").recv_string().await?,
@@ -95,50 +100,6 @@ async fn wild_last_segment() -> tide::Result<()> {
95100
Ok(())
96101
}
97102

98-
#[async_std::test]
99-
async fn invalid_wildcard() -> tide::Result<()> {
100-
let mut app = tide::new();
101-
app.at("/echo/*path/:one/").get(echo_path);
102-
assert_eq!(
103-
app.get("/echo/one/two").await?.status(),
104-
StatusCode::NotFound
105-
);
106-
Ok(())
107-
}
108-
109-
#[async_std::test]
110-
async fn nameless_wildcard() -> tide::Result<()> {
111-
let mut app = tide::Server::new();
112-
app.at("/echo/:").get(|_| async { Ok("") });
113-
assert_eq!(
114-
app.get("/echo/one/two").await?.status(),
115-
StatusCode::NotFound
116-
);
117-
assert_eq!(app.get("/echo/one").await?.status(), StatusCode::Ok);
118-
Ok(())
119-
}
120-
121-
#[async_std::test]
122-
async fn nameless_internal_wildcard() -> tide::Result<()> {
123-
let mut app = tide::new();
124-
app.at("/echo/:/:path").get(echo_path);
125-
assert_eq!(app.get("/echo/one").await?.status(), StatusCode::NotFound);
126-
assert_eq!(app.get("/echo/one/two").recv_string().await?, "two");
127-
Ok(())
128-
}
129-
130-
#[async_std::test]
131-
async fn nameless_internal_wildcard2() -> tide::Result<()> {
132-
let mut app = tide::new();
133-
app.at("/echo/:/:path").get(|req: Request<()>| async move {
134-
assert_eq!(req.param("path")?, "two");
135-
Ok("")
136-
});
137-
138-
assert!(app.get("/echo/one/two").await?.status().is_success());
139-
Ok(())
140-
}
141-
142103
#[async_std::test]
143104
async fn ambiguous_router_wildcard_vs_star() -> tide::Result<()> {
144105
let mut app = tide::new();

0 commit comments

Comments
 (0)