From 5c483ba6ae3ae4e033d2af8ee98dae30d99ed4c1 Mon Sep 17 00:00:00 2001 From: Kirk Turner Date: Tue, 21 May 2019 23:02:13 +0800 Subject: [PATCH] Fix the documentation for wildcards for path definitions to match implementation Also add a bunch of tests that demonstrate the implemented behavior --- tests/wildcard.rs | 169 +++++++++++++++++++++++++++++++++++++++++++ tide-core/src/app.rs | 19 +++-- 2 files changed, 180 insertions(+), 8 deletions(-) diff --git a/tests/wildcard.rs b/tests/wildcard.rs index 6d21ccf46..01c364620 100644 --- a/tests/wildcard.rs +++ b/tests/wildcard.rs @@ -10,6 +10,22 @@ async fn add_one(cx: Context<()>) -> Result { Ok((num + 1).to_string()) } +async fn add_two(cx: Context<()>) -> Result { + let one: i64 = cx.param("one").client_err()?; + let two: i64 = cx.param("two").client_err()?; + Ok((one + two).to_string()) +} + +async fn echo_path(cx: Context<()>) -> Result { + let path: String = cx.param("path").client_err()?; + Ok(path) +} + +async fn echo_empty(cx: Context<()>) -> Result { + let path: String = cx.param("").client_err()?; + Ok(path) +} + #[test] fn wildcard() { let mut app = tide::App::new(); @@ -56,3 +72,156 @@ fn not_found_error() { let res = server.simulate(req).unwrap(); assert_eq!(res.status(), 404); } + +#[test] +fn wildpath() { + let mut app = tide::App::new(); + app.at("/echo/*path").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/some_path") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"some_path"); + + let req = http::Request::get("/echo/multi/segment/path") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"multi/segment/path"); + + let req = http::Request::get("/echo/").body(Body::empty()).unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b""); +} + +#[test] +fn multi_wildcard() { + let mut app = tide::App::new(); + app.at("/add_two/:one/:two/").get(add_two); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/add_two/1/2/") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"3"); + + let req = http::Request::get("/add_two/-1/2/") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"1"); + let req = http::Request::get("/add_two/1") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); +} + +#[test] +fn wild_last_segment() { + let mut app = tide::App::new(); + app.at("/echo/:path/*").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"one"); + + let req = http::Request::get("/echo/one/two/three/four") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"one"); +} + +#[test] +fn invalid_wildcard() { + let mut app = tide::App::new(); + app.at("/echo/*path/:one/").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); +} + +#[test] +fn nameless_wildcard() { + let mut app = tide::App::new(); + app.at("/echo/:").get(async move |_| ""); + + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); + + let req = http::Request::get("/echo/one").body(Body::empty()).unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); +} + +#[test] +fn nameless_internal_wildcard() { + let mut app = tide::App::new(); + app.at("/echo/:/:path").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one").body(Body::empty()).unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"two"); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"two"); +} + +#[test] +fn nameless_internal_wildcard2() { + let mut app = tide::App::new(); + app.at("/echo/:/:path").get(echo_empty); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"one"); +} diff --git a/tide-core/src/app.rs b/tide-core/src/app.rs index e0e0549a0..8f7b64bc7 100644 --- a/tide-core/src/app.rs +++ b/tide-core/src/app.rs @@ -41,7 +41,7 @@ use crate::{ /// # Routing and parameters /// /// Tide's routing system is simple and similar to many other frameworks. It -/// uses `:foo` for "wildcard" URL segments, and `:foo*` to match the rest of a +/// uses `:foo` for "wildcard" URL segments, and `*foo` to match the rest of a /// URL (which may include multiple segments). Here's an example using wildcard /// segments as parameters to endpoints: /// @@ -183,12 +183,13 @@ impl App { /// parameter called `name`. It is not possible to define wildcard segments /// with different names for otherwise identical paths. /// - /// Wildcard definitions can be followed by an optional *wildcard - /// modifier*. Currently, there is only one modifier: `*`, which means that - /// the wildcard will match to the end of given path, no matter how many - /// segments are left, even nothing. It is an error to define two wildcard - /// segments with different wildcard modifiers, or to write other path - /// segment after a segment with wildcard modifier. + /// Alternatively a wildcard definitions can start with a `*`, for example + /// `*path`, which means that the wildcard will match to the end of given + /// path, no matter how many segments are left, even nothing. + /// + /// The name of the parameter can be omitted to define a path that matches + /// the required structure, but where the parameters are not required. + /// `:` will match a segment, and `*` will match an entire path. /// /// Here are some examples omitting the HTTP verb based endpoint selection: /// @@ -197,7 +198,9 @@ impl App { /// app.at("/"); /// app.at("/hello"); /// app.at("add_two/:num"); - /// app.at("static/:path*"); + /// app.at("files/:user/*"); + /// app.at("static/*path"); + /// app.at("static/:context/:"); /// ``` /// /// There is no fallback route matching, i.e. either a resource is a full