summaryrefslogtreecommitdiff
path: root/vendor/matchit/tests
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
committermo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
commit8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch)
tree22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/matchit/tests
parent4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff)
chore: add vendor directory
Diffstat (limited to 'vendor/matchit/tests')
-rw-r--r--vendor/matchit/tests/insert.rs243
-rw-r--r--vendor/matchit/tests/match.rs1047
-rw-r--r--vendor/matchit/tests/remove.rs265
3 files changed, 1555 insertions, 0 deletions
diff --git a/vendor/matchit/tests/insert.rs b/vendor/matchit/tests/insert.rs
new file mode 100644
index 00000000..5513c1c6
--- /dev/null
+++ b/vendor/matchit/tests/insert.rs
@@ -0,0 +1,243 @@
+use matchit::{InsertError, Router};
+
+struct InsertTest(Vec<(&'static str, Result<(), InsertError>)>);
+
+impl InsertTest {
+ fn run(self) {
+ let mut router = Router::new();
+ for (route, expected) in self.0 {
+ let got = router.insert(route, route.to_owned());
+ assert_eq!(got, expected, "{route}");
+ }
+ }
+}
+
+fn conflict(with: &'static str) -> InsertError {
+ InsertError::Conflict { with: with.into() }
+}
+
+#[test]
+fn wildcard_conflict() {
+ InsertTest(vec![
+ ("/cmd/{tool}/{sub}", Ok(())),
+ ("/cmd/vet", Ok(())),
+ ("/foo/bar", Ok(())),
+ ("/foo/{name}", Ok(())),
+ ("/foo/{names}", Err(conflict("/foo/{name}"))),
+ ("/cmd/{*path}", Err(conflict("/cmd/{tool}/{sub}"))),
+ ("/cmd/{xxx}/names", Ok(())),
+ ("/cmd/{tool}/{xxx}/foo", Ok(())),
+ ("/src/{*filepath}", Ok(())),
+ ("/src/{file}", Err(conflict("/src/{*filepath}"))),
+ ("/src/static.json", Ok(())),
+ ("/src/$filepathx", Ok(())),
+ ("/src/", Ok(())),
+ ("/src/foo/bar", Ok(())),
+ ("/src1/", Ok(())),
+ ("/src1/{*filepath}", Ok(())),
+ ("/src2{*filepath}", Ok(())),
+ ("/src2/{*filepath}", Ok(())),
+ ("/src2/", Ok(())),
+ ("/src2", Ok(())),
+ ("/src3", Ok(())),
+ ("/src3/{*filepath}", Ok(())),
+ ("/search/{query}", Ok(())),
+ ("/search/valid", Ok(())),
+ ("/user_{name}", Ok(())),
+ ("/user_x", Ok(())),
+ ("/user_{bar}", Err(conflict("/user_{name}"))),
+ ("/id{id}", Ok(())),
+ ("/id/{id}", Ok(())),
+ ])
+ .run()
+}
+
+#[test]
+fn invalid_catchall() {
+ InsertTest(vec![
+ ("/non-leading-{*catchall}", Ok(())),
+ ("/foo/bar{*catchall}", Ok(())),
+ ("/src/{*filepath}/x", Err(InsertError::InvalidCatchAll)),
+ ("/src2/", Ok(())),
+ ("/src2/{*filepath}/x", Err(InsertError::InvalidCatchAll)),
+ ])
+ .run()
+}
+
+#[test]
+fn catchall_root_conflict() {
+ InsertTest(vec![("/", Ok(())), ("/{*filepath}", Ok(()))]).run()
+}
+
+#[test]
+fn child_conflict() {
+ InsertTest(vec![
+ ("/cmd/vet", Ok(())),
+ ("/cmd/{tool}", Ok(())),
+ ("/cmd/{tool}/{sub}", Ok(())),
+ ("/cmd/{tool}/misc", Ok(())),
+ ("/cmd/{tool}/{bad}", Err(conflict("/cmd/{tool}/{sub}"))),
+ ("/src/AUTHORS", Ok(())),
+ ("/src/{*filepath}", Ok(())),
+ ("/user_x", Ok(())),
+ ("/user_{name}", Ok(())),
+ ("/id/{id}", Ok(())),
+ ("/id{id}", Ok(())),
+ ("/{id}", Ok(())),
+ ("/{*filepath}", Err(conflict("/{id}"))),
+ ])
+ .run()
+}
+
+#[test]
+fn duplicates() {
+ InsertTest(vec![
+ ("/", Ok(())),
+ ("/", Err(conflict("/"))),
+ ("/doc/", Ok(())),
+ ("/doc/", Err(conflict("/doc/"))),
+ ("/src/{*filepath}", Ok(())),
+ ("/src/{*filepath}", Err(conflict("/src/{*filepath}"))),
+ ("/search/{query}", Ok(())),
+ ("/search/{query}", Err(conflict("/search/{query}"))),
+ ("/user_{name}", Ok(())),
+ ("/user_{name}", Err(conflict("/user_{name}"))),
+ ])
+ .run()
+}
+
+#[test]
+fn unnamed_param() {
+ InsertTest(vec![
+ ("/{}", Err(InsertError::InvalidParam)),
+ ("/user{}/", Err(InsertError::InvalidParam)),
+ ("/cmd/{}/", Err(InsertError::InvalidParam)),
+ ("/src/{*}", Err(InsertError::InvalidParam)),
+ ])
+ .run()
+}
+
+#[test]
+fn double_params() {
+ InsertTest(vec![
+ ("/{foo}{bar}", Err(InsertError::InvalidParamSegment)),
+ ("/{foo}{bar}/", Err(InsertError::InvalidParamSegment)),
+ ("/{foo}{{*bar}/", Err(InsertError::InvalidParamSegment)),
+ ])
+ .run()
+}
+
+#[test]
+fn normalized_conflict() {
+ InsertTest(vec![
+ ("/x/{foo}/bar", Ok(())),
+ ("/x/{bar}/bar", Err(conflict("/x/{foo}/bar"))),
+ ("/{y}/bar/baz", Ok(())),
+ ("/{y}/baz/baz", Ok(())),
+ ("/{z}/bar/bat", Ok(())),
+ ("/{z}/bar/baz", Err(conflict("/{y}/bar/baz"))),
+ ])
+ .run()
+}
+
+#[test]
+fn more_conflicts() {
+ InsertTest(vec![
+ ("/con{tact}", Ok(())),
+ ("/who/are/{*you}", Ok(())),
+ ("/who/foo/hello", Ok(())),
+ ("/whose/{users}/{name}", Ok(())),
+ ("/who/are/foo", Ok(())),
+ ("/who/are/foo/bar", Ok(())),
+ ("/con{nection}", Err(conflict("/con{tact}"))),
+ (
+ "/whose/{users}/{user}",
+ Err(conflict("/whose/{users}/{name}")),
+ ),
+ ])
+ .run()
+}
+
+#[test]
+fn catchall_static_overlap() {
+ InsertTest(vec![
+ ("/bar", Ok(())),
+ ("/bar/", Ok(())),
+ ("/bar/{*foo}", Ok(())),
+ ])
+ .run();
+
+ InsertTest(vec![
+ ("/foo", Ok(())),
+ ("/{*bar}", Ok(())),
+ ("/bar", Ok(())),
+ ("/baz", Ok(())),
+ ("/baz/{split}", Ok(())),
+ ("/", Ok(())),
+ ("/{*bar}", Err(conflict("/{*bar}"))),
+ ("/{*zzz}", Err(conflict("/{*bar}"))),
+ ("/{xxx}", Err(conflict("/{*bar}"))),
+ ])
+ .run();
+
+ InsertTest(vec![
+ ("/{*bar}", Ok(())),
+ ("/bar", Ok(())),
+ ("/bar/x", Ok(())),
+ ("/bar_{x}", Ok(())),
+ ("/bar_{x}", Err(conflict("/bar_{x}"))),
+ ("/bar_{x}/y", Ok(())),
+ ("/bar/{x}", Ok(())),
+ ])
+ .run();
+}
+
+#[test]
+fn duplicate_conflict() {
+ InsertTest(vec![
+ ("/hey", Ok(())),
+ ("/hey/users", Ok(())),
+ ("/hey/user", Ok(())),
+ ("/hey/user", Err(conflict("/hey/user"))),
+ ])
+ .run()
+}
+
+#[test]
+fn invalid_param() {
+ InsertTest(vec![
+ ("{", Err(InsertError::InvalidParam)),
+ ("}", Err(InsertError::InvalidParam)),
+ ("x{y", Err(InsertError::InvalidParam)),
+ ("x}", Err(InsertError::InvalidParam)),
+ ("/{foo}s", Err(InsertError::InvalidParamSegment)),
+ ])
+ .run();
+}
+
+#[test]
+fn escaped_param() {
+ InsertTest(vec![
+ ("{{", Ok(())),
+ ("}}", Ok(())),
+ ("xx}}", Ok(())),
+ ("}}yy", Ok(())),
+ ("}}yy{{}}", Ok(())),
+ ("}}yy{{}}{{}}y{{", Ok(())),
+ ("}}yy{{}}{{}}y{{", Err(conflict("}yy{}{}y{"))),
+ ("/{{yy", Ok(())),
+ ("/{yy}", Ok(())),
+ ("/foo", Ok(())),
+ ("/foo/{{", Ok(())),
+ ("/foo/{{/{x}", Ok(())),
+ ("/foo/{ba{{r}", Ok(())),
+ ("/bar/{ba}}r}", Ok(())),
+ ("/xxx/{x{{}}y}", Ok(())),
+ ])
+ .run()
+}
+
+#[test]
+fn bare_catchall() {
+ InsertTest(vec![("{*foo}", Ok(())), ("foo/{*bar}", Ok(()))]).run()
+}
diff --git a/vendor/matchit/tests/match.rs b/vendor/matchit/tests/match.rs
new file mode 100644
index 00000000..90130fbc
--- /dev/null
+++ b/vendor/matchit/tests/match.rs
@@ -0,0 +1,1047 @@
+use matchit::{MatchError, Router};
+
+// https://github.com/ibraheemdev/matchit/issues/22
+#[test]
+fn partial_overlap() {
+ let mut x = Router::new();
+ x.insert("/foo_bar", "Welcome!").unwrap();
+ x.insert("/foo/bar", "Welcome!").unwrap();
+ assert_eq!(x.at("/foo/").unwrap_err(), MatchError::NotFound);
+
+ let mut x = Router::new();
+ x.insert("/foo", "Welcome!").unwrap();
+ x.insert("/foo/bar", "Welcome!").unwrap();
+ assert_eq!(x.at("/foo/").unwrap_err(), MatchError::NotFound);
+}
+
+// https://github.com/ibraheemdev/matchit/issues/31
+#[test]
+fn wildcard_overlap() {
+ let mut router = Router::new();
+ router.insert("/path/foo", "foo").unwrap();
+ router.insert("/path/{*rest}", "wildcard").unwrap();
+
+ assert_eq!(router.at("/path/foo").map(|m| *m.value), Ok("foo"));
+ assert_eq!(router.at("/path/bar").map(|m| *m.value), Ok("wildcard"));
+ assert_eq!(router.at("/path/foo/").map(|m| *m.value), Ok("wildcard"));
+
+ let mut router = Router::new();
+ router.insert("/path/foo/{arg}", "foo").unwrap();
+ router.insert("/path/{*rest}", "wildcard").unwrap();
+
+ assert_eq!(router.at("/path/foo/myarg").map(|m| *m.value), Ok("foo"));
+ assert_eq!(
+ router.at("/path/foo/myarg/").map(|m| *m.value),
+ Ok("wildcard")
+ );
+ assert_eq!(
+ router.at("/path/foo/myarg/bar/baz").map(|m| *m.value),
+ Ok("wildcard")
+ );
+}
+
+// https://github.com/ibraheemdev/matchit/issues/12
+#[test]
+fn overlapping_param_backtracking() {
+ let mut matcher = Router::new();
+
+ matcher.insert("/{object}/{id}", "object with id").unwrap();
+ matcher
+ .insert("/secret/{id}/path", "secret with id and path")
+ .unwrap();
+
+ let matched = matcher.at("/secret/978/path").unwrap();
+ assert_eq!(matched.params.get("id"), Some("978"));
+
+ let matched = matcher.at("/something/978").unwrap();
+ assert_eq!(matched.params.get("id"), Some("978"));
+ assert_eq!(matched.params.get("object"), Some("something"));
+
+ let matched = matcher.at("/secret/978").unwrap();
+ assert_eq!(matched.params.get("id"), Some("978"));
+}
+
+struct MatchTest {
+ routes: Vec<&'static str>,
+ matches: Vec<(
+ &'static str,
+ &'static str,
+ Result<Vec<(&'static str, &'static str)>, ()>,
+ )>,
+}
+
+impl MatchTest {
+ fn run(self) {
+ let mut router = Router::new();
+
+ for route in self.routes {
+ assert_eq!(router.insert(route, route.to_owned()), Ok(()), "{route}");
+ }
+
+ router.check_priorities().unwrap();
+
+ for (path, route, params) in self.matches {
+ match router.at(path) {
+ Ok(x) => {
+ assert_eq!(x.value, route);
+
+ let got = x.params.iter().collect::<Vec<_>>();
+ assert_eq!(params.unwrap(), got);
+
+ router.at_mut(path).unwrap().value.push_str("Z");
+ assert!(router.at(path).unwrap().value.contains("Z"));
+ router.at_mut(path).unwrap().value.pop();
+ }
+ Err(_) => params.unwrap_err(),
+ }
+ }
+ }
+}
+
+macro_rules! p {
+ ($($k:expr => $v:expr),* $(,)?) => {
+ Ok(vec![$(($k, $v)),*])
+ };
+}
+
+// https://github.com/ibraheemdev/matchit/issues/42
+#[test]
+fn bare_catchall() {
+ MatchTest {
+ routes: vec!["{*foo}", "foo/{*bar}"],
+ matches: vec![
+ ("x/y", "{*foo}", p! { "foo" => "x/y" }),
+ ("/x/y", "{*foo}", p! { "foo" => "/x/y" }),
+ ("/foo/x/y", "{*foo}", p! { "foo" => "/foo/x/y" }),
+ ("foo/x/y", "foo/{*bar}", p! { "bar" => "x/y" }),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn normalized() {
+ MatchTest {
+ routes: vec![
+ "/x/{foo}/bar",
+ "/x/{bar}/baz",
+ "/{foo}/{baz}/bax",
+ "/{foo}/{bar}/baz",
+ "/{fod}/{baz}/{bax}/foo",
+ "/{fod}/baz/bax/foo",
+ "/{foo}/baz/bax",
+ "/{bar}/{bay}/bay",
+ "/s",
+ "/s/s",
+ "/s/s/s",
+ "/s/s/s/s",
+ "/s/s/{s}/x",
+ "/s/s/{y}/d",
+ ],
+ matches: vec![
+ ("/x/foo/bar", "/x/{foo}/bar", p! { "foo" => "foo" }),
+ ("/x/foo/baz", "/x/{bar}/baz", p! { "bar" => "foo" }),
+ (
+ "/y/foo/baz",
+ "/{foo}/{bar}/baz",
+ p! { "foo" => "y", "bar" => "foo" },
+ ),
+ (
+ "/y/foo/bax",
+ "/{foo}/{baz}/bax",
+ p! { "foo" => "y", "baz" => "foo" },
+ ),
+ (
+ "/y/baz/baz",
+ "/{foo}/{bar}/baz",
+ p! { "foo" => "y", "bar" => "baz" },
+ ),
+ ("/y/baz/bax/foo", "/{fod}/baz/bax/foo", p! { "fod" => "y" }),
+ (
+ "/y/baz/b/foo",
+ "/{fod}/{baz}/{bax}/foo",
+ p! { "fod" => "y", "baz" => "baz", "bax" => "b" },
+ ),
+ ("/y/baz/bax", "/{foo}/baz/bax", p! { "foo" => "y" }),
+ (
+ "/z/bar/bay",
+ "/{bar}/{bay}/bay",
+ p! { "bar" => "z", "bay" => "bar" },
+ ),
+ ("/s", "/s", p! {}),
+ ("/s/s", "/s/s", p! {}),
+ ("/s/s/s", "/s/s/s", p! {}),
+ ("/s/s/s/s", "/s/s/s/s", p! {}),
+ ("/s/s/s/x", "/s/s/{s}/x", p! { "s" => "s" }),
+ ("/s/s/s/d", "/s/s/{y}/d", p! { "y" => "s" }),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn blog() {
+ MatchTest {
+ routes: vec![
+ "/{page}",
+ "/posts/{year}/{month}/{post}",
+ "/posts/{year}/{month}/index",
+ "/posts/{year}/top",
+ "/static/{*path}",
+ "/favicon.ico",
+ ],
+ matches: vec![
+ ("/about", "/{page}", p! { "page" => "about" }),
+ (
+ "/posts/2021/01/rust",
+ "/posts/{year}/{month}/{post}",
+ p! { "year" => "2021", "month" => "01", "post" => "rust" },
+ ),
+ (
+ "/posts/2021/01/index",
+ "/posts/{year}/{month}/index",
+ p! { "year" => "2021", "month" => "01" },
+ ),
+ (
+ "/posts/2021/top",
+ "/posts/{year}/top",
+ p! { "year" => "2021" },
+ ),
+ (
+ "/static/foo.png",
+ "/static/{*path}",
+ p! { "path" => "foo.png" },
+ ),
+ ("/favicon.ico", "/favicon.ico", p! {}),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn double_overlap() {
+ MatchTest {
+ routes: vec![
+ "/{object}/{id}",
+ "/secret/{id}/path",
+ "/secret/978",
+ "/other/{object}/{id}/",
+ "/other/an_object/{id}",
+ "/other/static/path",
+ "/other/long/static/path/",
+ ],
+ matches: vec![
+ (
+ "/secret/978/path",
+ "/secret/{id}/path",
+ p! { "id" => "978" },
+ ),
+ (
+ "/some_object/978",
+ "/{object}/{id}",
+ p! { "object" => "some_object", "id" => "978" },
+ ),
+ ("/secret/978", "/secret/978", p! {}),
+ ("/super_secret/978/", "/{object}/{id}", Err(())),
+ (
+ "/other/object/1/",
+ "/other/{object}/{id}/",
+ p! { "object" => "object", "id" => "1" },
+ ),
+ ("/other/object/1/2", "/other/{object}/{id}", Err(())),
+ (
+ "/other/an_object/1",
+ "/other/an_object/{id}",
+ p! { "id" => "1" },
+ ),
+ ("/other/static/path", "/other/static/path", p! {}),
+ (
+ "/other/long/static/path/",
+ "/other/long/static/path/",
+ p! {},
+ ),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn catchall_off_by_one() {
+ MatchTest {
+ routes: vec!["/foo/{*catchall}", "/bar", "/bar/", "/bar/{*catchall}"],
+ matches: vec![
+ ("/foo", "", Err(())),
+ ("/foo/", "", Err(())),
+ ("/foo/x", "/foo/{*catchall}", p! { "catchall" => "x" }),
+ ("/bar", "/bar", p! {}),
+ ("/bar/", "/bar/", p! {}),
+ ("/bar/x", "/bar/{*catchall}", p! { "catchall" => "x" }),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn overlap() {
+ MatchTest {
+ routes: vec![
+ "/foo",
+ "/bar",
+ "/{*bar}",
+ "/baz",
+ "/baz/",
+ "/baz/x",
+ "/baz/{xxx}",
+ "/",
+ "/xxx/{*x}",
+ "/xxx/",
+ ],
+ matches: vec![
+ ("/foo", "/foo", p! {}),
+ ("/bar", "/bar", p! {}),
+ ("/baz", "/baz", p! {}),
+ ("/baz/", "/baz/", p! {}),
+ ("/baz/x", "/baz/x", p! {}),
+ ("/???", "/{*bar}", p! { "bar" => "???" }),
+ ("/", "/", p! {}),
+ ("", "", Err(())),
+ ("/xxx/y", "/xxx/{*x}", p! { "x" => "y" }),
+ ("/xxx/", "/xxx/", p! {}),
+ ("/xxx", "/{*bar}", p! { "bar" => "xxx" }),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn missing_trailing_slash_param() {
+ MatchTest {
+ routes: vec!["/foo/{object}/{id}", "/foo/bar/baz", "/foo/secret/978/"],
+ matches: vec![
+ ("/foo/secret/978/", "/foo/secret/978/", p! {}),
+ (
+ "/foo/secret/978",
+ "/foo/{object}/{id}",
+ p! { "object" => "secret", "id" => "978" },
+ ),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn extra_trailing_slash_param() {
+ MatchTest {
+ routes: vec!["/foo/{object}/{id}", "/foo/bar/baz", "/foo/secret/978"],
+ matches: vec![
+ ("/foo/secret/978/", "", Err(())),
+ ("/foo/secret/978", "/foo/secret/978", p! {}),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn missing_trailing_slash_catch_all() {
+ MatchTest {
+ routes: vec!["/foo/{*bar}", "/foo/bar/baz", "/foo/secret/978/"],
+ matches: vec![
+ (
+ "/foo/secret/978",
+ "/foo/{*bar}",
+ p! { "bar" => "secret/978" },
+ ),
+ ("/foo/secret/978/", "/foo/secret/978/", p! {}),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn extra_trailing_slash_catch_all() {
+ MatchTest {
+ routes: vec!["/foo/{*bar}", "/foo/bar/baz", "/foo/secret/978"],
+ matches: vec![
+ (
+ "/foo/secret/978/",
+ "/foo/{*bar}",
+ p! { "bar" => "secret/978/" },
+ ),
+ ("/foo/secret/978", "/foo/secret/978", p! {}),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn double_overlap_trailing_slash() {
+ MatchTest {
+ routes: vec![
+ "/{object}/{id}",
+ "/secret/{id}/path",
+ "/secret/978/",
+ "/other/{object}/{id}/",
+ "/other/an_object/{id}",
+ "/other/static/path",
+ "/other/long/static/path/",
+ ],
+ matches: vec![
+ ("/secret/978/path/", "", Err(())),
+ ("/object/id/", "", Err(())),
+ ("/object/id/path", "", Err(())),
+ ("/other/object/1", "", Err(())),
+ ("/other/object/1/2", "", Err(())),
+ (
+ "/other/an_object/1/",
+ "/other/{object}/{id}/",
+ p! { "object" => "an_object", "id" => "1" },
+ ),
+ (
+ "/other/static/path/",
+ "/other/{object}/{id}/",
+ p! { "object" => "static", "id" => "path" },
+ ),
+ ("/other/long/static/path", "", Err(())),
+ ("/other/object/static/path", "", Err(())),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn trailing_slash_overlap() {
+ MatchTest {
+ routes: vec!["/foo/{x}/baz/", "/foo/{x}/baz", "/foo/bar/bar"],
+ matches: vec![
+ ("/foo/x/baz/", "/foo/{x}/baz/", p! { "x" => "x" }),
+ ("/foo/x/baz", "/foo/{x}/baz", p! { "x" => "x" }),
+ ("/foo/bar/bar", "/foo/bar/bar", p! {}),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn trailing_slash() {
+ MatchTest {
+ routes: vec![
+ "/hi",
+ "/b/",
+ "/search/{query}",
+ "/cmd/{tool}/",
+ "/src/{*filepath}",
+ "/x",
+ "/x/y",
+ "/y/",
+ "/y/z",
+ "/0/{id}",
+ "/0/{id}/1",
+ "/1/{id}/",
+ "/1/{id}/2",
+ "/aa",
+ "/a/",
+ "/admin",
+ "/admin/static",
+ "/admin/{category}",
+ "/admin/{category}/{page}",
+ "/doc",
+ "/doc/rust_faq.html",
+ "/doc/rust1.26.html",
+ "/no/a",
+ "/no/b",
+ "/no/a/b/{*other}",
+ "/api/{page}/{name}",
+ "/api/hello/{name}/bar/",
+ "/api/bar/{name}",
+ "/api/baz/foo",
+ "/api/baz/foo/bar",
+ "/foo/{p}",
+ ],
+ matches: vec![
+ ("/hi/", "", Err(())),
+ ("/b", "", Err(())),
+ ("/search/rustacean/", "", Err(())),
+ ("/cmd/vet", "", Err(())),
+ ("/src", "", Err(())),
+ ("/src/", "", Err(())),
+ ("/x/", "", Err(())),
+ ("/y", "", Err(())),
+ ("/0/rust/", "", Err(())),
+ ("/1/rust", "", Err(())),
+ ("/a", "", Err(())),
+ ("/admin/", "", Err(())),
+ ("/doc/", "", Err(())),
+ ("/admin/static/", "", Err(())),
+ ("/admin/cfg/", "", Err(())),
+ ("/admin/cfg/users/", "", Err(())),
+ ("/api/hello/x/bar", "", Err(())),
+ ("/api/baz/foo/", "", Err(())),
+ ("/api/baz/bax/", "", Err(())),
+ ("/api/bar/huh/", "", Err(())),
+ ("/api/baz/foo/bar/", "", Err(())),
+ ("/api/world/abc/", "", Err(())),
+ ("/foo/pp/", "", Err(())),
+ ("/", "", Err(())),
+ ("/no", "", Err(())),
+ ("/no/", "", Err(())),
+ ("/no/a/b", "", Err(())),
+ ("/no/a/b/", "", Err(())),
+ ("/_", "", Err(())),
+ ("/_/", "", Err(())),
+ ("/api", "", Err(())),
+ ("/api/", "", Err(())),
+ ("/api/hello/x/foo", "", Err(())),
+ ("/api/baz/foo/bad", "", Err(())),
+ ("/foo/p/p", "", Err(())),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn backtracking_trailing_slash() {
+ MatchTest {
+ routes: vec!["/a/{b}/{c}", "/a/b/{c}/d/"],
+ matches: vec![("/a/b/c/d", "", Err(()))],
+ }
+ .run()
+}
+
+#[test]
+fn root_trailing_slash() {
+ MatchTest {
+ routes: vec!["/foo", "/bar", "/{baz}"],
+ matches: vec![("/", "", Err(()))],
+ }
+ .run()
+}
+
+#[test]
+fn catchall_overlap() {
+ MatchTest {
+ routes: vec!["/yyy/{*x}", "/yyy{*x}"],
+ matches: vec![
+ ("/yyy/y", "/yyy/{*x}", p! { "x" => "y" }),
+ ("/yyy/", "/yyy{*x}", p! { "x" => "/" }),
+ ],
+ }
+ .run();
+}
+
+#[test]
+fn escaped() {
+ MatchTest {
+ routes: vec![
+ "/",
+ "/{{",
+ "/}}",
+ "/{{x",
+ "/}}y{{",
+ "/xy{{",
+ "/{{/xyz",
+ "/{ba{{r}",
+ "/{ba{{r}/",
+ "/{ba{{r}/x",
+ "/baz/{xxx}",
+ "/baz/{xxx}/xy{{",
+ "/baz/{xxx}/}}xy{{{{",
+ "/{{/{x}",
+ "/xxx/",
+ "/xxx/{x}}{{}}}}{{}}{{{{}}y}",
+ ],
+ matches: vec![
+ ("/", "/", p! {}),
+ ("/{", "/{{", p! {}),
+ ("/}", "/}}", p! {}),
+ ("/{x", "/{{x", p! {}),
+ ("/}y{", "/}}y{{", p! {}),
+ ("/xy{", "/xy{{", p! {}),
+ ("/{/xyz", "/{{/xyz", p! {}),
+ ("/foo", "/{ba{{r}", p! { "ba{r" => "foo" }),
+ ("/{{", "/{ba{{r}", p! { "ba{r" => "{{" }),
+ ("/{{}}/", "/{ba{{r}/", p! { "ba{r" => "{{}}" }),
+ ("/{{}}{{/x", "/{ba{{r}/x", p! { "ba{r" => "{{}}{{" }),
+ ("/baz/x", "/baz/{xxx}", p! { "xxx" => "x" }),
+ ("/baz/x/xy{", "/baz/{xxx}/xy{{", p! { "xxx" => "x" }),
+ ("/baz/x/xy{{", "", Err(())),
+ ("/baz/x/}xy{{", "/baz/{xxx}/}}xy{{{{", p! { "xxx" => "x" }),
+ ("/{/{{", "/{{/{x}", p! { "x" => "{{" }),
+ ("/xxx", "/{ba{{r}", p! { "ba{r" => "xxx" }),
+ ("/xxx/", "/xxx/", p!()),
+ (
+ "/xxx/foo",
+ "/xxx/{x}}{{}}}}{{}}{{{{}}y}",
+ p! { "x}{}}{}{{}y" => "foo" },
+ ),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn basic() {
+ MatchTest {
+ routes: vec![
+ "/hi",
+ "/contact",
+ "/co",
+ "/c",
+ "/a",
+ "/ab",
+ "/doc/",
+ "/doc/rust_faq.html",
+ "/doc/rust1.26.html",
+ "/ʯ",
+ "/β",
+ "/sd!here",
+ "/sd$here",
+ "/sd&here",
+ "/sd'here",
+ "/sd(here",
+ "/sd)here",
+ "/sd+here",
+ "/sd,here",
+ "/sd;here",
+ "/sd=here",
+ ],
+ matches: vec![
+ ("/a", "/a", p! {}),
+ ("", "/", Err(())),
+ ("/hi", "/hi", p! {}),
+ ("/contact", "/contact", p! {}),
+ ("/co", "/co", p! {}),
+ ("", "/con", Err(())),
+ ("", "/cona", Err(())),
+ ("", "/no", Err(())),
+ ("/ab", "/ab", p! {}),
+ ("/ʯ", "/ʯ", p! {}),
+ ("/β", "/β", p! {}),
+ ("/sd!here", "/sd!here", p! {}),
+ ("/sd$here", "/sd$here", p! {}),
+ ("/sd&here", "/sd&here", p! {}),
+ ("/sd'here", "/sd'here", p! {}),
+ ("/sd(here", "/sd(here", p! {}),
+ ("/sd)here", "/sd)here", p! {}),
+ ("/sd+here", "/sd+here", p! {}),
+ ("/sd,here", "/sd,here", p! {}),
+ ("/sd;here", "/sd;here", p! {}),
+ ("/sd=here", "/sd=here", p! {}),
+ ],
+ }
+ .run()
+}
+
+#[test]
+fn wildcard() {
+ MatchTest {
+ routes: vec![
+ "/",
+ "/cmd/{tool}/",
+ "/cmd/{tool2}/{sub}",
+ "/cmd/whoami",
+ "/cmd/whoami/root",
+ "/cmd/whoami/root/",
+ "/src",
+ "/src/",
+ "/src/{*filepath}",
+ "/search/",
+ "/search/{query}",
+ "/search/actix-web",
+ "/search/google",
+ "/user_{name}",
+ "/user_{name}/about",
+ "/files/{dir}/{*filepath}",
+ "/doc/",
+ "/doc/rust_faq.html",
+ "/doc/rust1.26.html",
+ "/info/{user}/public",
+ "/info/{user}/project/{project}",
+ "/info/{user}/project/rustlang",
+ "/aa/{*xx}",
+ "/ab/{*xx}",
+ "/ab/hello{*xx}",
+ "/{cc}",
+ "/c1/{dd}/e",
+ "/c1/{dd}/e1",
+ "/{cc}/cc",
+ "/{cc}/{dd}/ee",
+ "/{cc}/{dd}/{ee}/ff",
+ "/{cc}/{dd}/{ee}/{ff}/gg",
+ "/{cc}/{dd}/{ee}/{ff}/{gg}/hh",
+ "/get/test/abc/",
+ "/get/{param}/abc/",
+ "/something/{paramname}/thirdthing",
+ "/something/secondthing/test",
+ "/get/abc",
+ "/get/{param}",
+ "/get/abc/123abc",
+ "/get/abc/{param}",
+ "/get/abc/123abc/xxx8",
+ "/get/abc/123abc/{param}",
+ "/get/abc/123abc/xxx8/1234",
+ "/get/abc/123abc/xxx8/{param}",
+ "/get/abc/123abc/xxx8/1234/ffas",
+ "/get/abc/123abc/xxx8/1234/{param}",
+ "/get/abc/123abc/xxx8/1234/kkdd/12c",
+ "/get/abc/123abc/xxx8/1234/kkdd/{param}",
+ "/get/abc/{param}/test",
+ "/get/abc/123abd/{param}",
+ "/get/abc/123abddd/{param}",
+ "/get/abc/123/{param}",
+ "/get/abc/123abg/{param}",
+ "/get/abc/123abf/{param}",
+ "/get/abc/123abfff/{param}",
+ ],
+ matches: vec![
+ ("/", "/", p! {}),
+ ("/cmd/test", "/cmd/{tool}/", Err(())),
+ ("/cmd/test/", "/cmd/{tool}/", p! { "tool" => "test" }),
+ (
+ "/cmd/test/3",
+ "/cmd/{tool2}/{sub}",
+ p! { "tool2" => "test", "sub" => "3" },
+ ),
+ ("/cmd/who", "/cmd/{tool}/", Err(())),
+ ("/cmd/who/", "/cmd/{tool}/", p! { "tool" => "who" }),
+ ("/cmd/whoami", "/cmd/whoami", p! {}),
+ ("/cmd/whoami/", "/cmd/{tool}/", p! { "tool" => "whoami" }),
+ (
+ "/cmd/whoami/r",
+ "/cmd/{tool2}/{sub}",
+ p! { "tool2" => "whoami", "sub" => "r" },
+ ),
+ ("/cmd/whoami/r/", "/cmd/{tool}/{sub}", Err(())),
+ ("/cmd/whoami/root", "/cmd/whoami/root", p! {}),
+ ("/cmd/whoami/root/", "/cmd/whoami/root/", p! {}),
+ ("/src", "/src", p! {}),
+ ("/src/", "/src/", p! {}),
+ (
+ "/src/some/file.png",
+ "/src/{*filepath}",
+ p! { "filepath" => "some/file.png" },
+ ),
+ ("/search/", "/search/", p! {}),
+ (
+ "/search/actix",
+ "/search/{query}",
+ p! { "query" => "actix" },
+ ),
+ ("/search/actix-web", "/search/actix-web", p! {}),
+ (
+ "/search/someth!ng+in+ünìcodé",
+ "/search/{query}",
+ p! { "query" => "someth!ng+in+ünìcodé" },
+ ),
+ ("/search/someth!ng+in+ünìcodé/", "", Err(())),
+ (
+ "/user_rustacean",
+ "/user_{name}",
+ p! { "name" => "rustacean" },
+ ),
+ (
+ "/user_rustacean/about",
+ "/user_{name}/about",
+ p! { "name" => "rustacean" },
+ ),
+ (
+ "/files/js/inc/framework.js",
+ "/files/{dir}/{*filepath}",
+ p! { "dir" => "js", "filepath" => "inc/framework.js" },
+ ),
+ (
+ "/info/gordon/public",
+ "/info/{user}/public",
+ p! { "user" => "gordon" },
+ ),
+ (
+ "/info/gordon/project/rust",
+ "/info/{user}/project/{project}",
+ p! { "user" => "gordon", "project" => "rust" },
+ ),
+ (
+ "/info/gordon/project/rustlang",
+ "/info/{user}/project/rustlang",
+ p! { "user" => "gordon" },
+ ),
+ ("/aa/", "/", Err(())),
+ ("/aa/aa", "/aa/{*xx}", p! { "xx" => "aa" }),
+ ("/ab/ab", "/ab/{*xx}", p! { "xx" => "ab" }),
+ ("/ab/hello-world", "/ab/hello{*xx}", p! { "xx" => "-world" }),
+ ("/a", "/{cc}", p! { "cc" => "a" }),
+ ("/all", "/{cc}", p! { "cc" => "all" }),
+ ("/d", "/{cc}", p! { "cc" => "d" }),
+ ("/ad", "/{cc}", p! { "cc" => "ad" }),
+ ("/dd", "/{cc}", p! { "cc" => "dd" }),
+ ("/dddaa", "/{cc}", p! { "cc" => "dddaa" }),
+ ("/aa", "/{cc}", p! { "cc" => "aa" }),
+ ("/aaa", "/{cc}", p! { "cc" => "aaa" }),
+ ("/aaa/cc", "/{cc}/cc", p! { "cc" => "aaa" }),
+ ("/ab", "/{cc}", p! { "cc" => "ab" }),
+ ("/abb", "/{cc}", p! { "cc" => "abb" }),
+ ("/abb/cc", "/{cc}/cc", p! { "cc" => "abb" }),
+ ("/allxxxx", "/{cc}", p! { "cc" => "allxxxx" }),
+ ("/alldd", "/{cc}", p! { "cc" => "alldd" }),
+ ("/all/cc", "/{cc}/cc", p! { "cc" => "all" }),
+ ("/a/cc", "/{cc}/cc", p! { "cc" => "a" }),
+ ("/c1/d/e", "/c1/{dd}/e", p! { "dd" => "d" }),
+ ("/c1/d/e1", "/c1/{dd}/e1", p! { "dd" => "d" }),
+ (
+ "/c1/d/ee",
+ "/{cc}/{dd}/ee",
+ p! { "cc" => "c1", "dd" => "d" },
+ ),
+ ("/cc/cc", "/{cc}/cc", p! { "cc" => "cc" }),
+ ("/ccc/cc", "/{cc}/cc", p! { "cc" => "ccc" }),
+ ("/deedwjfs/cc", "/{cc}/cc", p! { "cc" => "deedwjfs" }),
+ ("/acllcc/cc", "/{cc}/cc", p! { "cc" => "acllcc" }),
+ ("/get/test/abc/", "/get/test/abc/", p! {}),
+ ("/get/te/abc/", "/get/{param}/abc/", p! { "param" => "te" }),
+ (
+ "/get/testaa/abc/",
+ "/get/{param}/abc/",
+ p! { "param" => "testaa" },
+ ),
+ ("/get/xx/abc/", "/get/{param}/abc/", p! { "param" => "xx" }),
+ ("/get/tt/abc/", "/get/{param}/abc/", p! { "param" => "tt" }),
+ ("/get/a/abc/", "/get/{param}/abc/", p! { "param" => "a" }),
+ ("/get/t/abc/", "/get/{param}/abc/", p! { "param" => "t" }),
+ ("/get/aa/abc/", "/get/{param}/abc/", p! { "param" => "aa" }),
+ (
+ "/get/abas/abc/",
+ "/get/{param}/abc/",
+ p! { "param" => "abas" },
+ ),
+ (
+ "/something/secondthing/test",
+ "/something/secondthing/test",
+ p! {},
+ ),
+ (
+ "/something/abcdad/thirdthing",
+ "/something/{paramname}/thirdthing",
+ p! { "paramname" => "abcdad" },
+ ),
+ (
+ "/something/secondthingaaaa/thirdthing",
+ "/something/{paramname}/thirdthing",
+ p! { "paramname" => "secondthingaaaa" },
+ ),
+ (
+ "/something/se/thirdthing",
+ "/something/{paramname}/thirdthing",
+ p! { "paramname" => "se" },
+ ),
+ (
+ "/something/s/thirdthing",
+ "/something/{paramname}/thirdthing",
+ p! { "paramname" => "s" },
+ ),
+ ("/c/d/ee", "/{cc}/{dd}/ee", p! { "cc" => "c", "dd" => "d" }),
+ (
+ "/c/d/e/ff",
+ "/{cc}/{dd}/{ee}/ff",
+ p! { "cc" => "c", "dd" => "d", "ee" => "e" },
+ ),
+ (
+ "/c/d/e/f/gg",
+ "/{cc}/{dd}/{ee}/{ff}/gg",
+ p! { "cc" => "c", "dd" => "d", "ee" => "e", "ff" => "f" },
+ ),
+ (
+ "/c/d/e/f/g/hh",
+ "/{cc}/{dd}/{ee}/{ff}/{gg}/hh",
+ p! { "cc" => "c", "dd" => "d", "ee" => "e", "ff" => "f", "gg" => "g" },
+ ),
+ (
+ "/cc/dd/ee/ff/gg/hh",
+ "/{cc}/{dd}/{ee}/{ff}/{gg}/hh",
+ p! { "cc" => "cc", "dd" => "dd", "ee" => "ee", "ff" => "ff", "gg" => "gg" },
+ ),
+ ("/get/abc", "/get/abc", p! {}),
+ ("/get/a", "/get/{param}", p! { "param" => "a" }),
+ ("/get/abz", "/get/{param}", p! { "param" => "abz" }),
+ ("/get/12a", "/get/{param}", p! { "param" => "12a" }),
+ ("/get/abcd", "/get/{param}", p! { "param" => "abcd" }),
+ ("/get/abc/123abc", "/get/abc/123abc", p! {}),
+ ("/get/abc/12", "/get/abc/{param}", p! { "param" => "12" }),
+ (
+ "/get/abc/123ab",
+ "/get/abc/{param}",
+ p! { "param" => "123ab" },
+ ),
+ ("/get/abc/xyz", "/get/abc/{param}", p! { "param" => "xyz" }),
+ (
+ "/get/abc/123abcddxx",
+ "/get/abc/{param}",
+ p! { "param" => "123abcddxx" },
+ ),
+ ("/get/abc/123abc/xxx8", "/get/abc/123abc/xxx8", p! {}),
+ (
+ "/get/abc/123abc/x",
+ "/get/abc/123abc/{param}",
+ p! { "param" => "x" },
+ ),
+ (
+ "/get/abc/123abc/xxx",
+ "/get/abc/123abc/{param}",
+ p! { "param" => "xxx" },
+ ),
+ (
+ "/get/abc/123abc/abc",
+ "/get/abc/123abc/{param}",
+ p! { "param" => "abc" },
+ ),
+ (
+ "/get/abc/123abc/xxx8xxas",
+ "/get/abc/123abc/{param}",
+ p! { "param" => "xxx8xxas" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234",
+ "/get/abc/123abc/xxx8/1234",
+ p! {},
+ ),
+ (
+ "/get/abc/123abc/xxx8/1",
+ "/get/abc/123abc/xxx8/{param}",
+ p! { "param" => "1" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/123",
+ "/get/abc/123abc/xxx8/{param}",
+ p! { "param" => "123" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/78k",
+ "/get/abc/123abc/xxx8/{param}",
+ p! { "param" => "78k" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234xxxd",
+ "/get/abc/123abc/xxx8/{param}",
+ p! { "param" => "1234xxxd" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/ffas",
+ "/get/abc/123abc/xxx8/1234/ffas",
+ p! {},
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/f",
+ "/get/abc/123abc/xxx8/1234/{param}",
+ p! { "param" => "f" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/ffa",
+ "/get/abc/123abc/xxx8/1234/{param}",
+ p! { "param" => "ffa" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kka",
+ "/get/abc/123abc/xxx8/1234/{param}",
+ p! { "param" => "kka" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/ffas321",
+ "/get/abc/123abc/xxx8/1234/{param}",
+ p! { "param" => "ffas321" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kkdd/12c",
+ "/get/abc/123abc/xxx8/1234/kkdd/12c",
+ p! {},
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kkdd/1",
+ "/get/abc/123abc/xxx8/1234/kkdd/{param}",
+ p! { "param" => "1" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kkdd/12",
+ "/get/abc/123abc/xxx8/1234/kkdd/{param}",
+ p! { "param" => "12" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kkdd/12b",
+ "/get/abc/123abc/xxx8/1234/kkdd/{param}",
+ p! { "param" => "12b" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kkdd/34",
+ "/get/abc/123abc/xxx8/1234/kkdd/{param}",
+ p! { "param" => "34" },
+ ),
+ (
+ "/get/abc/123abc/xxx8/1234/kkdd/12c2e3",
+ "/get/abc/123abc/xxx8/1234/kkdd/{param}",
+ p! { "param" => "12c2e3" },
+ ),
+ (
+ "/get/abc/12/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "12" },
+ ),
+ (
+ "/get/abc/123abdd/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "123abdd" },
+ ),
+ (
+ "/get/abc/123abdddf/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "123abdddf" },
+ ),
+ (
+ "/get/abc/123ab/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "123ab" },
+ ),
+ (
+ "/get/abc/123abgg/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "123abgg" },
+ ),
+ (
+ "/get/abc/123abff/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "123abff" },
+ ),
+ (
+ "/get/abc/123abffff/test",
+ "/get/abc/{param}/test",
+ p! { "param" => "123abffff" },
+ ),
+ (
+ "/get/abc/123abd/test",
+ "/get/abc/123abd/{param}",
+ p! { "param" => "test" },
+ ),
+ (
+ "/get/abc/123abddd/test",
+ "/get/abc/123abddd/{param}",
+ p! { "param" => "test" },
+ ),
+ (
+ "/get/abc/123/test22",
+ "/get/abc/123/{param}",
+ p! { "param" => "test22" },
+ ),
+ (
+ "/get/abc/123abg/test",
+ "/get/abc/123abg/{param}",
+ p! { "param" => "test" },
+ ),
+ (
+ "/get/abc/123abf/testss",
+ "/get/abc/123abf/{param}",
+ p! { "param" => "testss" },
+ ),
+ (
+ "/get/abc/123abfff/te",
+ "/get/abc/123abfff/{param}",
+ p! { "param" => "te" },
+ ),
+ ],
+ }
+ .run()
+}
diff --git a/vendor/matchit/tests/remove.rs b/vendor/matchit/tests/remove.rs
new file mode 100644
index 00000000..3237ccde
--- /dev/null
+++ b/vendor/matchit/tests/remove.rs
@@ -0,0 +1,265 @@
+use matchit::Router;
+
+struct RemoveTest {
+ routes: Vec<&'static str>,
+ ops: Vec<(Operation, &'static str, Option<&'static str>)>,
+ remaining: Vec<&'static str>,
+}
+
+enum Operation {
+ Insert,
+ Remove,
+}
+
+use Operation::*;
+
+impl RemoveTest {
+ fn run(self) {
+ let mut router = Router::new();
+
+ for route in self.routes.iter() {
+ assert_eq!(router.insert(*route, route.to_owned()), Ok(()), "{route}");
+ }
+
+ for (op, route, expected) in self.ops.iter() {
+ match op {
+ Insert => {
+ assert_eq!(router.insert(*route, route), Ok(()), "{route}")
+ }
+ Remove => {
+ assert_eq!(router.remove(*route), *expected, "removing {route}",)
+ }
+ }
+ }
+
+ for route in self.remaining {
+ assert!(matches!(router.at(route), Ok(_)), "remaining {route}");
+ }
+ }
+}
+
+#[test]
+fn normalized() {
+ RemoveTest {
+ routes: vec![
+ "/x/{foo}/bar",
+ "/x/{bar}/baz",
+ "/{foo}/{baz}/bax",
+ "/{foo}/{bar}/baz",
+ "/{fod}/{baz}/{bax}/foo",
+ "/{fod}/baz/bax/foo",
+ "/{foo}/baz/bax",
+ "/{bar}/{bay}/bay",
+ "/s",
+ "/s/s",
+ "/s/s/s",
+ "/s/s/s/s",
+ "/s/s/{s}/x",
+ "/s/s/{y}/d",
+ ],
+ ops: vec![
+ (Remove, "/x/{foo}/bar", Some("/x/{foo}/bar")),
+ (Remove, "/x/{bar}/baz", Some("/x/{bar}/baz")),
+ (Remove, "/{foo}/{baz}/bax", Some("/{foo}/{baz}/bax")),
+ (Remove, "/{foo}/{bar}/baz", Some("/{foo}/{bar}/baz")),
+ (
+ Remove,
+ "/{fod}/{baz}/{bax}/foo",
+ Some("/{fod}/{baz}/{bax}/foo"),
+ ),
+ (Remove, "/{fod}/baz/bax/foo", Some("/{fod}/baz/bax/foo")),
+ (Remove, "/{foo}/baz/bax", Some("/{foo}/baz/bax")),
+ (Remove, "/{bar}/{bay}/bay", Some("/{bar}/{bay}/bay")),
+ (Remove, "/s", Some("/s")),
+ (Remove, "/s/s", Some("/s/s")),
+ (Remove, "/s/s/s", Some("/s/s/s")),
+ (Remove, "/s/s/s/s", Some("/s/s/s/s")),
+ (Remove, "/s/s/{s}/x", Some("/s/s/{s}/x")),
+ (Remove, "/s/s/{y}/d", Some("/s/s/{y}/d")),
+ ],
+ remaining: vec![],
+ }
+ .run();
+}
+
+#[test]
+fn test() {
+ RemoveTest {
+ routes: vec!["/home", "/home/{id}"],
+ ops: vec![
+ (Remove, "/home", Some("/home")),
+ (Remove, "/home", None),
+ (Remove, "/home/{id}", Some("/home/{id}")),
+ (Remove, "/home/{id}", None),
+ ],
+ remaining: vec![],
+ }
+ .run();
+}
+
+#[test]
+fn blog() {
+ RemoveTest {
+ routes: vec![
+ "/{page}",
+ "/posts/{year}/{month}/{post}",
+ "/posts/{year}/{month}/index",
+ "/posts/{year}/top",
+ "/static/{*path}",
+ "/favicon.ico",
+ ],
+ ops: vec![
+ (Remove, "/{page}", Some("/{page}")),
+ (
+ Remove,
+ "/posts/{year}/{month}/{post}",
+ Some("/posts/{year}/{month}/{post}"),
+ ),
+ (
+ Remove,
+ "/posts/{year}/{month}/index",
+ Some("/posts/{year}/{month}/index"),
+ ),
+ (Remove, "/posts/{year}/top", Some("/posts/{year}/top")),
+ (Remove, "/static/{*path}", Some("/static/{*path}")),
+ (Remove, "/favicon.ico", Some("/favicon.ico")),
+ ],
+ remaining: vec![],
+ }
+ .run()
+}
+
+#[test]
+fn catchall() {
+ RemoveTest {
+ routes: vec!["/foo/{*catchall}", "/bar", "/bar/", "/bar/{*catchall}"],
+ ops: vec![
+ (Remove, "/foo/{*catchall}", Some("/foo/{*catchall}")),
+ (Remove, "/bar/", Some("/bar/")),
+ (Insert, "/foo/*catchall", Some("/foo/*catchall")),
+ (Remove, "/bar/{*catchall}", Some("/bar/{*catchall}")),
+ ],
+ remaining: vec!["/bar", "/foo/*catchall"],
+ }
+ .run();
+}
+
+#[test]
+fn overlapping_routes() {
+ RemoveTest {
+ routes: vec![
+ "/home",
+ "/home/{id}",
+ "/users",
+ "/users/{id}",
+ "/users/{id}/posts",
+ "/users/{id}/posts/{post_id}",
+ "/articles",
+ "/articles/{category}",
+ "/articles/{category}/{id}",
+ ],
+ ops: vec![
+ (Remove, "/home", Some("/home")),
+ (Insert, "/home", Some("/home")),
+ (Remove, "/home/{id}", Some("/home/{id}")),
+ (Insert, "/home/{id}", Some("/home/{id}")),
+ (Remove, "/users", Some("/users")),
+ (Insert, "/users", Some("/users")),
+ (Remove, "/users/{id}", Some("/users/{id}")),
+ (Insert, "/users/{id}", Some("/users/{id}")),
+ (Remove, "/users/{id}/posts", Some("/users/{id}/posts")),
+ (Insert, "/users/{id}/posts", Some("/users/{id}/posts")),
+ (
+ Remove,
+ "/users/{id}/posts/{post_id}",
+ Some("/users/{id}/posts/{post_id}"),
+ ),
+ (
+ Insert,
+ "/users/{id}/posts/{post_id}",
+ Some("/users/{id}/posts/{post_id}"),
+ ),
+ (Remove, "/articles", Some("/articles")),
+ (Insert, "/articles", Some("/articles")),
+ (Remove, "/articles/{category}", Some("/articles/{category}")),
+ (Insert, "/articles/{category}", Some("/articles/{category}")),
+ (
+ Remove,
+ "/articles/{category}/{id}",
+ Some("/articles/{category}/{id}"),
+ ),
+ (
+ Insert,
+ "/articles/{category}/{id}",
+ Some("/articles/{category}/{id}"),
+ ),
+ ],
+ remaining: vec![
+ "/home",
+ "/home/{id}",
+ "/users",
+ "/users/{id}",
+ "/users/{id}/posts",
+ "/users/{id}/posts/{post_id}",
+ "/articles",
+ "/articles/{category}",
+ "/articles/{category}/{id}",
+ ],
+ }
+ .run();
+}
+
+#[test]
+fn trailing_slash() {
+ RemoveTest {
+ routes: vec!["/{home}/", "/foo"],
+ ops: vec![
+ (Remove, "/", None),
+ (Remove, "/{home}", None),
+ (Remove, "/foo/", None),
+ (Remove, "/foo", Some("/foo")),
+ (Remove, "/{home}", None),
+ (Remove, "/{home}/", Some("/{home}/")),
+ ],
+ remaining: vec![],
+ }
+ .run();
+}
+
+#[test]
+fn remove_root() {
+ RemoveTest {
+ routes: vec!["/"],
+ ops: vec![(Remove, "/", Some("/"))],
+ remaining: vec![],
+ }
+ .run();
+}
+
+#[test]
+fn check_escaped_params() {
+ RemoveTest {
+ routes: vec![
+ "/foo/{id}",
+ "/foo/{id}/bar",
+ "/bar/{user}/{id}",
+ "/bar/{user}/{id}/baz",
+ "/baz/{product}/{user}/{id}",
+ ],
+ ops: vec![
+ (Remove, "/foo/{a}", None),
+ (Remove, "/foo/{a}/bar", None),
+ (Remove, "/bar/{a}/{b}", None),
+ (Remove, "/bar/{a}/{b}/baz", None),
+ (Remove, "/baz/{a}/{b}/{c}", None),
+ ],
+ remaining: vec![
+ "/foo/{id}",
+ "/foo/{id}/bar",
+ "/bar/{user}/{id}",
+ "/bar/{user}/{id}/baz",
+ "/baz/{product}/{user}/{id}",
+ ],
+ }
+ .run();
+}