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, ()>, )>, } 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::>(); 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 empty_param() { MatchTest { routes: vec!["/y/{foo}", "/x/{foo}/z", "/z/{*xx}"], matches: vec![ ("/y/", "", Err(())), ("/x//z", "", Err(())), ("/z/", "", Err(())), ], } .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() }