Skip to content

Commit

Permalink
Fix the documentation for wildcards for path definitions to match imp…
Browse files Browse the repository at this point in the history
…lementation

Also add a bunch of tests that demonstrate the implemented behavior
  • Loading branch information
Kirk Turner committed May 21, 2019
1 parent 1a93fc2 commit 5c483ba
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 8 deletions.
169 changes: 169 additions & 0 deletions tests/wildcard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ async fn add_one(cx: Context<()>) -> Result<String, tide::Error> {
Ok((num + 1).to_string())
}

async fn add_two(cx: Context<()>) -> Result<String, tide::Error> {
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<String, tide::Error> {
let path: String = cx.param("path").client_err()?;
Ok(path)
}

async fn echo_empty(cx: Context<()>) -> Result<String, tide::Error> {
let path: String = cx.param("").client_err()?;
Ok(path)
}

#[test]
fn wildcard() {
let mut app = tide::App::new();
Expand Down Expand Up @@ -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");
}
19 changes: 11 additions & 8 deletions tide-core/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
///
Expand Down Expand Up @@ -183,12 +183,13 @@ impl<State: Send + Sync + 'static> App<State> {
/// 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:
///
Expand All @@ -197,7 +198,9 @@ impl<State: Send + Sync + 'static> App<State> {
/// 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
Expand Down

0 comments on commit 5c483ba

Please sign in to comment.