1 use matchit::{InsertError, MatchError, Router};
2 
3 #[test]
issue_31()4 fn issue_31() {
5     let mut router = Router::new();
6     router.insert("/path/foo/:arg", "foo").unwrap();
7     router.insert("/path/*rest", "wildcard").unwrap();
8 
9     assert_eq!(
10         router.at("/path/foo/myarg/bar/baz").map(|m| *m.value),
11         Ok("wildcard")
12     );
13 }
14 
15 #[test]
issue_22()16 fn issue_22() {
17     let mut x = Router::new();
18     x.insert("/foo_bar", "Welcome!").unwrap();
19     x.insert("/foo/bar", "Welcome!").unwrap();
20     assert_eq!(x.at("/foo/").unwrap_err(), MatchError::NotFound);
21 
22     let mut x = Router::new();
23     x.insert("/foo", "Welcome!").unwrap();
24     x.insert("/foo/bar", "Welcome!").unwrap();
25     assert_eq!(x.at("/foo/").unwrap_err(), MatchError::ExtraTrailingSlash);
26 }
27 
28 match_tests! {
29     basic {
30         routes = [
31             "/hi",
32             "/contact",
33             "/co",
34             "/c",
35             "/a",
36             "/ab",
37             "/doc/",
38             "/doc/rust_faq.html",
39             "/doc/rust1.26.html",
40             "/ʯ",
41             "/β",
42             "/sd!here",
43             "/sd$here",
44             "/sd&here",
45             "/sd'here",
46             "/sd(here",
47             "/sd)here",
48             "/sd+here",
49             "/sd,here",
50             "/sd;here",
51             "/sd=here",
52         ],
53         "/a"       :: "/a"       => {},
54         ""         :: "/"        => None,
55         "/hi"      :: "/hi"      => {},
56         "/contact" :: "/contact" => {},
57         "/co"      :: "/co"      => {},
58         ""         :: "/con"     => None,
59         ""         :: "/cona"    => None,
60         ""         :: "/no"      => None,
61         "/ab"      :: "/ab"      => {},
62         "/ʯ"       :: "/ʯ"       => {},
63         "/β"       :: "/β"       => {},
64         "/sd!here" :: "/sd!here" => {},
65         "/sd$here" :: "/sd$here" => {},
66         "/sd&here" :: "/sd&here" => {},
67         "/sd'here" :: "/sd'here" => {},
68         "/sd(here" :: "/sd(here" => {},
69         "/sd)here" :: "/sd)here" => {},
70         "/sd+here" :: "/sd+here" => {},
71         "/sd,here" :: "/sd,here" => {},
72         "/sd;here" :: "/sd;here" => {},
73         "/sd=here" :: "/sd=here" => {},
74     },
75     wildcard {
76         routes = [
77             "/",
78             "/cmd/:tool/",
79             "/cmd/:tool2/:sub",
80             "/cmd/whoami",
81             "/cmd/whoami/root",
82             "/cmd/whoami/root/",
83             "/src",
84             "/src/",
85             "/src/*filepath",
86             "/search/",
87             "/search/:query",
88             "/search/actix-web",
89             "/search/google",
90             "/user_:name",
91             "/user_:name/about",
92             "/files/:dir/*filepath",
93             "/doc/",
94             "/doc/rust_faq.html",
95             "/doc/rust1.26.html",
96             "/info/:user/public",
97             "/info/:user/project/:project",
98             "/info/:user/project/rustlang",
99             "/aa/*xx",
100             "/ab/*xx",
101             "/:cc",
102             "/c1/:dd/e",
103             "/c1/:dd/e1",
104             "/:cc/cc",
105             "/:cc/:dd/ee",
106             "/:cc/:dd/:ee/ff",
107             "/:cc/:dd/:ee/:ff/gg",
108             "/:cc/:dd/:ee/:ff/:gg/hh",
109             "/get/test/abc/",
110             "/get/:param/abc/",
111             "/something/:paramname/thirdthing",
112             "/something/secondthing/test",
113             "/get/abc",
114             "/get/:param",
115             "/get/abc/123abc",
116             "/get/abc/:param",
117             "/get/abc/123abc/xxx8",
118             "/get/abc/123abc/:param",
119             "/get/abc/123abc/xxx8/1234",
120             "/get/abc/123abc/xxx8/:param",
121             "/get/abc/123abc/xxx8/1234/ffas",
122             "/get/abc/123abc/xxx8/1234/:param",
123             "/get/abc/123abc/xxx8/1234/kkdd/12c",
124             "/get/abc/123abc/xxx8/1234/kkdd/:param",
125             "/get/abc/:param/test",
126             "/get/abc/123abd/:param",
127             "/get/abc/123abddd/:param",
128             "/get/abc/123/:param",
129             "/get/abc/123abg/:param",
130             "/get/abc/123abf/:param",
131             "/get/abc/123abfff/:param",
132         ],
133         "/"                                     :: "/"                                     => {},
134         "/cmd/test"                             :: "/cmd/:tool/"                           => None,
135         "/cmd/test/"                            :: "/cmd/:tool/"                           => { "tool" => "test" },
136         "/cmd/test/3"                           :: "/cmd/:tool2/:sub"                       => { "tool2" => "test", "sub" => "3" },
137         "/cmd/who"                              :: "/cmd/:tool/"                           => None,
138         "/cmd/who/"                             :: "/cmd/:tool/"                           => { "tool" => "who" },
139         "/cmd/whoami"                           :: "/cmd/whoami"                           => {},
140         "/cmd/whoami/"                          :: "/cmd/whoami"                           => None,
141         "/cmd/whoami/r"                         :: "/cmd/:tool2/:sub"                       => { "tool2" => "whoami", "sub" => "r" },
142         "/cmd/whoami/r/"                        :: "/cmd/:tool/:sub"                       => None,
143         "/cmd/whoami/root"                      :: "/cmd/whoami/root"                      => {},
144         "/cmd/whoami/root/"                     :: "/cmd/whoami/root/"                     => {},
145         "/src"                                  :: "/src"                                  => {},
146         "/src/"                                 :: "/src/"                                 => {},
147         "/src/some/file.png"                    :: "/src/*filepath"                        => { "filepath" => "some/file.png" },
148         "/search/"                              :: "/search/"                              => {},
149         "/search/actix"                         :: "/search/:query"                        => { "query" => "actix" },
150         "/search/actix-web"                     :: "/search/actix-web"                     => {},
151         "/search/someth!ng+in+ünìcodé"          :: "/search/:query"                        => { "query" => "someth!ng+in+ünìcodé" },
152         "/search/someth!ng+in+ünìcodé/"         :: ""                                      => None,
153         "/user_rustacean"                       :: "/user_:name"                           => { "name" => "rustacean" },
154         "/user_rustacean/about"                 :: "/user_:name/about"                     => { "name" => "rustacean" },
155         "/files/js/inc/framework.js"            :: "/files/:dir/*filepath"                 => { "dir" => "js", "filepath" => "inc/framework.js" },
156         "/info/gordon/public"                   :: "/info/:user/public"                    => { "user" => "gordon" },
157         "/info/gordon/project/rust"             :: "/info/:user/project/:project"          => { "user" => "gordon", "project" => "rust" } ,
158         "/info/gordon/project/rustlang"         :: "/info/:user/project/rustlang"          => { "user" => "gordon" },
159         "/aa/"                                  :: "/"                                     => None,
160         "/aa/aa"                                :: "/aa/*xx"                               => { "xx" => "aa" },
161         "/ab/ab"                                :: "/ab/*xx"                               => { "xx" => "ab" },
162         "/a"                                    :: "/:cc"                                  => { "cc" => "a" },
163         "/all"                                  :: "/:cc"                                  => { "cc" => "all" },
164         "/d"                                    :: "/:cc"                                  => { "cc" => "d" },
165         "/ad"                                   :: "/:cc"                                  => { "cc" => "ad" },
166         "/dd"                                   :: "/:cc"                                  => { "cc" => "dd" },
167         "/dddaa"                                :: "/:cc"                                  => { "cc" => "dddaa" },
168         "/aa"                                   :: "/:cc"                                  => { "cc" => "aa" },
169         "/aaa"                                  :: "/:cc"                                  => { "cc" => "aaa" },
170         "/aaa/cc"                               :: "/:cc/cc"                               => { "cc" => "aaa" },
171         "/ab"                                   :: "/:cc"                                  => { "cc" => "ab" },
172         "/abb"                                  :: "/:cc"                                  => { "cc" => "abb" },
173         "/abb/cc"                               :: "/:cc/cc"                               => { "cc" => "abb" },
174         "/allxxxx"                              :: "/:cc"                                  => { "cc" => "allxxxx" },
175         "/alldd"                                :: "/:cc"                                  => { "cc" => "alldd" },
176         "/all/cc"                               :: "/:cc/cc"                               => { "cc" => "all" },
177         "/a/cc"                                 :: "/:cc/cc"                               => { "cc" => "a" },
178         "/c1/d/e"                               :: "/c1/:dd/e"                             => { "dd" => "d" },
179         "/c1/d/e1"                              :: "/c1/:dd/e1"                            => { "dd" => "d" },
180         "/c1/d/ee"                              :: "/:cc/:dd/ee"                           => { "cc" => "c1", "dd" => "d" },
181         "/cc/cc"                                :: "/:cc/cc"                               => { "cc" => "cc" },
182         "/ccc/cc"                               :: "/:cc/cc"                               => { "cc" => "ccc" },
183         "/deedwjfs/cc"                          :: "/:cc/cc"                               => { "cc" => "deedwjfs" },
184         "/acllcc/cc"                            :: "/:cc/cc"                               => { "cc" => "acllcc" },
185         "/get/test/abc/"                        :: "/get/test/abc/"                        => {},
186         "/get/te/abc/"                          :: "/get/:param/abc/"                      => { "param" => "te" },
187         "/get/testaa/abc/"                      :: "/get/:param/abc/"                      => { "param" => "testaa" },
188         "/get/xx/abc/"                          :: "/get/:param/abc/"                      => { "param" => "xx" },
189         "/get/tt/abc/"                          :: "/get/:param/abc/"                      => { "param" => "tt" },
190         "/get/a/abc/"                           :: "/get/:param/abc/"                      => { "param" => "a" },
191         "/get/t/abc/"                           :: "/get/:param/abc/"                      => { "param" => "t" },
192         "/get/aa/abc/"                          :: "/get/:param/abc/"                      => { "param" => "aa" },
193         "/get/abas/abc/"                        :: "/get/:param/abc/"                      => { "param" => "abas" },
194         "/something/secondthing/test"           :: "/something/secondthing/test"           => {},
195         "/something/abcdad/thirdthing"          :: "/something/:paramname/thirdthing"      => { "paramname" => "abcdad" },
196         "/something/secondthingaaaa/thirdthing" :: "/something/:paramname/thirdthing"      => { "paramname" => "secondthingaaaa" },
197         "/something/se/thirdthing"              :: "/something/:paramname/thirdthing"      => { "paramname" => "se" },
198         "/something/s/thirdthing"               :: "/something/:paramname/thirdthing"      => { "paramname" => "s" },
199         "/c/d/ee"                               :: "/:cc/:dd/ee"                           => { "cc" => "c", "dd" => "d" },
200         "/c/d/e/ff"                             :: "/:cc/:dd/:ee/ff"                       => { "cc" => "c", "dd" => "d", "ee" => "e" },
201         "/c/d/e/f/gg"                           :: "/:cc/:dd/:ee/:ff/gg"                   => { "cc" => "c", "dd" => "d", "ee" => "e", "ff" => "f" },
202         "/c/d/e/f/g/hh"                         :: "/:cc/:dd/:ee/:ff/:gg/hh"               => { "cc" => "c", "dd" => "d", "ee" => "e", "ff" => "f", "gg" => "g" },
203         "/cc/dd/ee/ff/gg/hh"                    :: "/:cc/:dd/:ee/:ff/:gg/hh"               => { "cc" => "cc", "dd" => "dd", "ee" => "ee", "ff" => "ff", "gg" => "gg" },
204         "/get/abc"                              :: "/get/abc"                              => {},
205         "/get/a"                                :: "/get/:param"                           => { "param" => "a" },
206         "/get/abz"                              :: "/get/:param"                           => { "param" => "abz" },
207         "/get/12a"                              :: "/get/:param"                           => { "param" => "12a" },
208         "/get/abcd"                             :: "/get/:param"                           => { "param" => "abcd" },
209         "/get/abc/123abc"                       :: "/get/abc/123abc"                       => {},
210         "/get/abc/12"                           :: "/get/abc/:param"                       => { "param" => "12" },
211         "/get/abc/123ab"                        :: "/get/abc/:param"                       => { "param" => "123ab" },
212         "/get/abc/xyz"                          :: "/get/abc/:param"                       => { "param" => "xyz" },
213         "/get/abc/123abcddxx"                   :: "/get/abc/:param"                       => { "param" => "123abcddxx" },
214         "/get/abc/123abc/xxx8"                  :: "/get/abc/123abc/xxx8"                  => {},
215         "/get/abc/123abc/x"                     :: "/get/abc/123abc/:param"                => { "param" => "x" },
216         "/get/abc/123abc/xxx"                   :: "/get/abc/123abc/:param"                => { "param" => "xxx" },
217         "/get/abc/123abc/abc"                   :: "/get/abc/123abc/:param"                => { "param" => "abc" },
218         "/get/abc/123abc/xxx8xxas"              :: "/get/abc/123abc/:param"                => { "param" => "xxx8xxas" },
219         "/get/abc/123abc/xxx8/1234"             :: "/get/abc/123abc/xxx8/1234"             => {},
220         "/get/abc/123abc/xxx8/1"                :: "/get/abc/123abc/xxx8/:param"           => { "param" => "1" },
221         "/get/abc/123abc/xxx8/123"              :: "/get/abc/123abc/xxx8/:param"           => { "param" => "123" },
222         "/get/abc/123abc/xxx8/78k"              :: "/get/abc/123abc/xxx8/:param"           => { "param" => "78k" },
223         "/get/abc/123abc/xxx8/1234xxxd"         :: "/get/abc/123abc/xxx8/:param"           => { "param" => "1234xxxd" },
224         "/get/abc/123abc/xxx8/1234/ffas"        :: "/get/abc/123abc/xxx8/1234/ffas"        => {},
225         "/get/abc/123abc/xxx8/1234/f"           :: "/get/abc/123abc/xxx8/1234/:param"      => { "param" => "f" },
226         "/get/abc/123abc/xxx8/1234/ffa"         :: "/get/abc/123abc/xxx8/1234/:param"      => { "param" => "ffa" },
227         "/get/abc/123abc/xxx8/1234/kka"         :: "/get/abc/123abc/xxx8/1234/:param"      => { "param" => "kka" },
228         "/get/abc/123abc/xxx8/1234/ffas321"     :: "/get/abc/123abc/xxx8/1234/:param"      => { "param" => "ffas321" },
229         "/get/abc/123abc/xxx8/1234/kkdd/12c"    :: "/get/abc/123abc/xxx8/1234/kkdd/12c"    => {},
230         "/get/abc/123abc/xxx8/1234/kkdd/1"      :: "/get/abc/123abc/xxx8/1234/kkdd/:param" => { "param" => "1" },
231         "/get/abc/123abc/xxx8/1234/kkdd/12"     :: "/get/abc/123abc/xxx8/1234/kkdd/:param" => { "param" => "12" },
232         "/get/abc/123abc/xxx8/1234/kkdd/12b"    :: "/get/abc/123abc/xxx8/1234/kkdd/:param" => { "param" => "12b" },
233         "/get/abc/123abc/xxx8/1234/kkdd/34"     :: "/get/abc/123abc/xxx8/1234/kkdd/:param" => { "param" => "34" },
234         "/get/abc/123abc/xxx8/1234/kkdd/12c2e3" :: "/get/abc/123abc/xxx8/1234/kkdd/:param" => { "param" => "12c2e3" },
235         "/get/abc/12/test"                      :: "/get/abc/:param/test"                  => { "param" => "12" },
236         "/get/abc/123abdd/test"                 :: "/get/abc/:param/test"                  => { "param" => "123abdd" },
237         "/get/abc/123abdddf/test"               :: "/get/abc/:param/test"                  => { "param" => "123abdddf" },
238         "/get/abc/123ab/test"                   :: "/get/abc/:param/test"                  => { "param" => "123ab" },
239         "/get/abc/123abgg/test"                 :: "/get/abc/:param/test"                  => { "param" => "123abgg" },
240         "/get/abc/123abff/test"                 :: "/get/abc/:param/test"                  => { "param" => "123abff" },
241         "/get/abc/123abffff/test"               :: "/get/abc/:param/test"                  => { "param" => "123abffff" },
242         "/get/abc/123abd/test"                  :: "/get/abc/123abd/:param"                => { "param" => "test" },
243         "/get/abc/123abddd/test"                :: "/get/abc/123abddd/:param"              => { "param" => "test" },
244         "/get/abc/123/test22"                   :: "/get/abc/123/:param"                   => { "param" => "test22" },
245         "/get/abc/123abg/test"                  :: "/get/abc/123abg/:param"                => { "param" => "test" },
246         "/get/abc/123abf/testss"                :: "/get/abc/123abf/:param"                => { "param" => "testss" },
247         "/get/abc/123abfff/te"                  :: "/get/abc/123abfff/:param"              => { "param" => "te" },
248     },
249     normalized {
250         routes = [
251             "/x/:foo/bar",
252             "/x/:bar/baz",
253             "/:foo/:baz/bax",
254             "/:foo/:bar/baz",
255             "/:fod/:baz/:bax/foo",
256             "/:fod/baz/bax/foo",
257             "/:foo/baz/bax",
258             "/:bar/:bay/bay",
259             "/s",
260             "/s/s",
261             "/s/s/s",
262             "/s/s/s/s",
263             "/s/s/:s/x",
264             "/s/s/:y/d",
265         ],
266         "/x/foo/bar"      :: "/x/:foo/bar"         => { "foo" => "foo" },
267         "/x/foo/baz"      :: "/x/:bar/baz"         => { "bar" => "foo" },
268         "/y/foo/baz"      :: "/:foo/:bar/baz"      => { "foo" => "y", "bar" => "foo" },
269         "/y/foo/bax"      :: "/:foo/:baz/bax"      => { "foo" => "y", "baz" => "foo" },
270         "/y/baz/baz"      :: "/:foo/:bar/baz"      => { "foo" => "y", "bar" => "baz" },
271         "/y/baz/bax/foo"  :: "/:fod/baz/bax/foo"   => { "fod" => "y" },
272         "/y/baz/b/foo"    :: "/:fod/:baz/:bax/foo" => { "fod" => "y", "baz" => "baz", "bax" => "b" },
273         "/y/baz/bax"      :: "/:foo/baz/bax"       => { "foo" => "y" },
274         "/z/bar/bay"      :: "/:bar/:bay/bay"      => { "bar" => "z", "bay" => "bar" },
275         "/s"              :: "/s"                  => { },
276         "/s/s"            :: "/s/s"                => { },
277         "/s/s/s"          :: "/s/s/s"              => { },
278         "/s/s/s/s"        :: "/s/s/s/s"            => { },
279         "/s/s/s/x"        :: "/s/s/:s/x"           => { "s" => "s" },
280         "/s/s/s/d"        :: "/s/s/:y/d"           => { "y" => "s" },
281     },
282     blog {
283         routes = [
284             "/:page",
285             "/posts/:year/:month/:post",
286             "/posts/:year/:month/index",
287             "/posts/:year/top",
288             "/static/*path",
289             "/favicon.ico",
290         ],
291         "/about"                :: "/:page"                    => { "page" => "about" },
292         "/posts/2021/01/rust"   :: "/posts/:year/:month/:post" => { "year" => "2021", "month" => "01", "post" => "rust" },
293         "/posts/2021/01/index"  :: "/posts/:year/:month/index" => { "year" => "2021", "month" => "01" },
294         "/posts/2021/top"       :: "/posts/:year/top"          => { "year" => "2021" },
295         "/static/foo.png"       :: "/static/*path"             => { "path" => "foo.png" },
296         "/favicon.ico"          :: "/favicon.ico"              => {},
297     },
298     double_overlap {
299         routes = [
300             "/:object/:id",
301             "/secret/:id/path",
302             "/secret/978",
303             "/other/:object/:id/",
304             "/other/an_object/:id",
305             "/other/static/path",
306             "/other/long/static/path/"
307         ],
308         "/secret/978/path"                :: "/secret/:id/path"                    => { "id" => "978" },
309         "/some_object/978"                :: "/:object/:id"                        => { "object" => "some_object", "id" => "978" },
310         "/secret/978"                     :: "/secret/978"                         => {},
311         "/super_secret/978/"              :: "/:object/:id"                        => None,
312         "/other/object/1/"                :: "/other/:object/:id/"                 => { "object" => "object", "id" => "1" },
313         "/other/object/1/2"               :: "/other/:object/:id"                  => None,
314         "/other/an_object/1"              :: "/other/an_object/:id"                => { "id" => "1" },
315         "/other/static/path"              :: "/other/static/path"                  => {},
316         "/other/long/static/path/"        :: "/other/long/static/path/"            => {},
317     },
318     catchall_off_by_one {
319         routes = [
320             "/foo/*catchall",
321             "/bar",
322             "/bar/",
323             "/bar/*catchall",
324         ],
325         "/foo"   :: ""               => None,
326         "/foo/"  :: ""               => None,
327         "/foo/x" :: "/foo/*catchall" => { "catchall" => "x" },
328         "/bar"   :: "/bar"           => {},
329         "/bar/"  :: "/bar/"          => {},
330         "/bar/x" :: "/bar/*catchall" => { "catchall" => "x" },
331     },
332     catchall_static_overlap {
333         routes = [
334             "/foo",
335             "/bar",
336             "/*bar",
337             "/baz",
338             "/baz/",
339             "/baz/x",
340             "/baz/:xxx",
341             "/",
342             "/xxx/*x",
343             "/xxx/",
344         ],
345         "/foo"    :: "/foo"    => {},
346         "/bar"    :: "/bar"    => {},
347         "/baz"    :: "/baz"    => {},
348         "/baz/"   :: "/baz/"   => {},
349         "/baz/x"  :: "/baz/x"  => {},
350         "/???"    :: "/*bar"   => { "bar" => "???" },
351         "/"       :: "/"       => {},
352         ""        :: ""        => None,
353         "/xxx/y"  :: "/xxx/*x" => { "x" => "y" },
354         "/xxx/"   :: "/xxx/"   => {},
355         "/xxx"    :: ""        => None
356     }
357 }
358 
359 // https://github.com/ibraheemdev/matchit/issues/12
360 #[test]
issue_12()361 fn issue_12() {
362     let mut matcher = Router::new();
363 
364     matcher.insert("/:object/:id", "object with id").unwrap();
365     matcher
366         .insert("/secret/:id/path", "secret with id and path")
367         .unwrap();
368 
369     let matched = matcher.at("/secret/978/path").unwrap();
370     assert_eq!(matched.params.get("id"), Some("978"));
371 
372     let matched = matcher.at("/something/978").unwrap();
373     assert_eq!(matched.params.get("id"), Some("978"));
374     assert_eq!(matched.params.get("object"), Some("something"));
375 
376     let matched = matcher.at("/secret/978").unwrap();
377     assert_eq!(matched.params.get("id"), Some("978"));
378 }
379 
380 insert_tests! {
381     wildcard_conflict {
382         "/cmd/:tool/:sub"     => Ok(()),
383         "/cmd/vet"            => Ok(()),
384         "/foo/bar"            => Ok(()),
385         "/foo/:name"          => Ok(()),
386         "/foo/:names"         => Err(InsertError::Conflict { with: "/foo/:name".into() }),
387         "/cmd/*path"          => Err(InsertError::Conflict { with: "/cmd/:tool/:sub".into() }),
388         "/cmd/:xxx/names"     => Ok(()),
389         "/cmd/:tool/:xxx/foo" => Ok(()),
390         "/src/*filepath"      => Ok(()),
391         "/src/:file"          => Err(InsertError::Conflict { with: "/src/*filepath".into() }),
392         "/src/static.json"    => Ok(()),
393         "/src/$filepathx"     => Ok(()),
394         "/src/"               => Ok(()),
395         "/src/foo/bar"        => Ok(()),
396         "/src1/"              => Ok(()),
397         "/src1/*filepath"     => Ok(()),
398         "/src2*filepath"      => Err(InsertError::InvalidCatchAll),
399         "/src2/*filepath"     => Ok(()),
400         "/src2/"              => Ok(()),
401         "/src2"               => Ok(()),
402         "/src3"               => Ok(()),
403         "/src3/*filepath"     => Ok(()),
404         "/search/:query"      => Ok(()),
405         "/search/valid"       => Ok(()),
406         "/user_:name"         => Ok(()),
407         "/user_x"             => Ok(()),
408         "/user_:bar"          => Err(InsertError::Conflict { with: "/user_:name".into() }),
409         "/id:id"              => Ok(()),
410         "/id/:id"             => Ok(()),
411     },
412     invalid_catchall {
413         "/non-leading-*catchall" => Err(InsertError::InvalidCatchAll),
414         "/foo/bar*catchall"      => Err(InsertError::InvalidCatchAll),
415         "/src/*filepath/x"       => Err(InsertError::InvalidCatchAll),
416         "/src2/"                 => Ok(()),
417         "/src2/*filepath/x"      => Err(InsertError::InvalidCatchAll),
418     },
419     invalid_catchall2 {
420         "*x" => Err(InsertError::InvalidCatchAll)
421     },
422     catchall_root_conflict {
423         "/"          => Ok(()),
424         "/*filepath" => Ok(()),
425     },
426     child_conflict {
427         "/cmd/vet"        => Ok(()),
428         "/cmd/:tool"      => Ok(()),
429         "/cmd/:tool/:sub" => Ok(()),
430         "/cmd/:tool/misc" => Ok(()),
431         "/cmd/:tool/:bad" => Err(InsertError::Conflict { with: "/cmd/:tool/:sub".into() }),
432         "/src/AUTHORS"    => Ok(()),
433         "/src/*filepath"  => Ok(()),
434         "/user_x"         => Ok(()),
435         "/user_:name"     => Ok(()),
436         "/id/:id"         => Ok(()),
437         "/id:id"          => Ok(()),
438         "/:id"            => Ok(()),
439         "/*filepath"      => Err(InsertError::Conflict { with: "/:id".into() }),
440     },
441     duplicates {
442         "/"              => Ok(()),
443         "/"              => Err(InsertError::Conflict { with: "/".into() }),
444         "/doc/"          => Ok(()),
445         "/doc/"          => Err(InsertError::Conflict { with: "/doc/".into() }),
446         "/src/*filepath" => Ok(()),
447         "/src/*filepath" => Err(InsertError::Conflict { with: "/src/*filepath".into() }),
448         "/search/:query" => Ok(()),
449         "/search/:query" => Err(InsertError::Conflict { with: "/search/:query".into() }),
450         "/user_:name"    => Ok(()),
451         "/user_:name"    => Err(InsertError::Conflict { with: "/user_:name".into() }),
452     },
453     unnamed_param {
454         "/user:"  => Err(InsertError::UnnamedParam),
455         "/user:/" => Err(InsertError::UnnamedParam),
456         "/cmd/:/" => Err(InsertError::UnnamedParam),
457         "/src/*"  => Err(InsertError::UnnamedParam),
458     },
459     double_params {
460         "/:foo:bar"  => Err(InsertError::TooManyParams),
461         "/:foo:bar/" => Err(InsertError::TooManyParams),
462         "/:foo*bar/" => Err(InsertError::TooManyParams),
463     },
464     normalized_conflict {
465         "/x/:foo/bar"  => Ok(()),
466         "/x/:bar/bar"  => Err(InsertError::Conflict { with: "/x/:foo/bar".into() }),
467         "/:y/bar/baz"  => Ok(()),
468         "/:y/baz/baz"  => Ok(()),
469         "/:z/bar/bat"  => Ok(()),
470         "/:z/bar/baz"  => Err(InsertError::Conflict { with: "/:y/bar/baz".into() }),
471     },
472     more_conflicts {
473         "/con:tact"           => Ok(()),
474         "/who/are/*you"       => Ok(()),
475         "/who/foo/hello"      => Ok(()),
476         "/whose/:users/:name" => Ok(()),
477         "/who/are/foo"        => Ok(()),
478         "/who/are/foo/bar"    => Ok(()),
479         "/con:nection"        => Err(InsertError::Conflict { with: "/con:tact".into() }),
480         "/whose/:users/:user" => Err(InsertError::Conflict { with: "/whose/:users/:name".into() }),
481     },
482     catchall_static_overlap1 {
483         "/bar"      => Ok(()),
484         "/bar/"     => Ok(()),
485         "/bar/*foo" => Ok(()),
486     },
487     catchall_static_overlap2 {
488         "/foo"            => Ok(()),
489         "/*bar"           => Ok(()),
490         "/bar"            => Ok(()),
491         "/baz"            => Ok(()),
492         "/baz/:split"     => Ok(()),
493         "/"               => Ok(()),
494         "/*bar"           => Err(InsertError::Conflict { with: "/*bar".into() }),
495         "/*zzz"           => Err(InsertError::Conflict { with: "/*bar".into() }),
496         "/:xxx"           => Err(InsertError::Conflict { with: "/*bar".into() }),
497     },
498     catchall_static_overlap3 {
499         "/*bar"           => Ok(()),
500         "/bar"            => Ok(()),
501         "/bar/x"          => Ok(()),
502         "/bar_:x"         => Ok(()),
503         "/bar_:x"         => Err(InsertError::Conflict { with: "/bar_:x".into() }),
504         "/bar_:x/y"       => Ok(()),
505         "/bar/:x"         => Ok(()),
506     },
507 }
508 
509 tsr_tests! {
510     tsr {
511         routes = [
512             "/hi",
513             "/b/",
514             "/search/:query",
515             "/cmd/:tool/",
516             "/src/*filepath",
517             "/x",
518             "/x/y",
519             "/y/",
520             "/y/z",
521             "/0/:id",
522             "/0/:id/1",
523             "/1/:id/",
524             "/1/:id/2",
525             "/aa",
526             "/a/",
527             "/admin",
528             "/admin/static",
529             "/admin/:category",
530             "/admin/:category/:page",
531             "/doc",
532             "/doc/rust_faq.html",
533             "/doc/rust1.26.html",
534             "/no/a",
535             "/no/b",
536             "/no/a/b/*other",
537             "/api/:page/:name",
538             "/api/hello/:name/bar/",
539             "/api/bar/:name",
540             "/api/baz/foo",
541             "/api/baz/foo/bar",
542             "/foo/:p",
543         ],
544         "/hi/"               => ExtraTrailingSlash,
545         "/b"                 => MissingTrailingSlash,
546         "/search/rustacean/" => ExtraTrailingSlash,
547         "/cmd/vet"           => MissingTrailingSlash,
548         "/src"               => NotFound,
549         "/src/"              => NotFound,
550         "/x/"                => ExtraTrailingSlash,
551         "/y"                 => MissingTrailingSlash,
552         "/0/rust/"           => ExtraTrailingSlash,
553         "/1/rust"            => MissingTrailingSlash,
554         "/a"                 => MissingTrailingSlash,
555         "/admin/"            => ExtraTrailingSlash,
556         "/doc/"              => ExtraTrailingSlash,
557         "/admin/static/"     => ExtraTrailingSlash,
558         "/admin/cfg/"        => ExtraTrailingSlash,
559         "/admin/cfg/users/"  => ExtraTrailingSlash,
560         "/api/hello/x/bar"   => MissingTrailingSlash,
561         "/api/baz/foo/"      => ExtraTrailingSlash,
562         "/api/baz/bax/"      => ExtraTrailingSlash,
563         "/api/bar/huh/"      => ExtraTrailingSlash,
564         "/api/baz/foo/bar/"  => ExtraTrailingSlash,
565         "/api/world/abc/"    => ExtraTrailingSlash,
566         "/foo/pp/"           => ExtraTrailingSlash,
567         "/"                  => NotFound,
568         "/no"                => NotFound,
569         "/no/"               => NotFound,
570         "/no/a/b"            => NotFound,
571         "/no/a/b/"           => NotFound,
572         "/_"                 => NotFound,
573         "/_/"                => NotFound,
574         "/api"               => NotFound,
575         "/api/"              => NotFound,
576         "/api/hello/x/foo"   => NotFound,
577         "/api/baz/foo/bad"   => NotFound,
578         "/foo/p/p"           => NotFound,
579     },
580     backtracking_tsr {
581         routes = [
582             "/a/:b/:c",
583             "/a/b/:c/d/",
584         ],
585         "/a/b/c/d"   => MissingTrailingSlash,
586     },
587     same_len {
588         routes = ["/foo", "/bar/"],
589         "/baz" => NotFound,
590     },
591     root_tsr_wildcard {
592         routes = ["/:foo"],
593         "/" => NotFound,
594     },
595     root_tsr_static {
596         routes = ["/foo"],
597         "/" => NotFound,
598     },
599     root_tsr {
600         routes = [
601             "/foo",
602             "/bar",
603             "/:baz"
604         ],
605         "/" => NotFound,
606     },
607     double_overlap_tsr {
608         routes = [
609             "/:object/:id",
610             "/secret/:id/path",
611             "/secret/978/",
612             "/other/:object/:id/",
613             "/other/an_object/:id",
614             "/other/static/path",
615             "/other/long/static/path/"
616         ],
617         "/secret/978/path/"          => ExtraTrailingSlash,
618         "/object/id/"                => ExtraTrailingSlash,
619         "/object/id/path"            => NotFound,
620         "/secret/978"                => MissingTrailingSlash,
621         "/other/object/1"            => MissingTrailingSlash,
622         "/other/object/1/2"          => NotFound,
623         "/other/an_object/1/"        => ExtraTrailingSlash,
624         "/other/static/path/"        => ExtraTrailingSlash,
625         "/other/long/static/path"    => MissingTrailingSlash,
626         "/other/object/static/path"  => NotFound,
627     },
628 }
629 
630 macro_rules! match_tests {
631     ($($name:ident {
632         routes = $routes:expr,
633         $( $path:literal :: $route:literal =>
634             $( $(@$none:tt)? None )?
635             $( $(@$some:tt)? { $( $key:literal => $val:literal ),* $(,)? } )?
636         ),* $(,)?
637     }),* $(,)?) => { $(
638         #[test]
639         fn $name() {
640             let mut router = Router::new();
641 
642             for route in $routes {
643                 router.insert(route, route.to_owned())
644                     .unwrap_or_else(|e| panic!("error when inserting route '{}': {:?}", route, e));
645             }
646 
647             $(match router.at($path) {
648                 Err(_) => {
649                     $($( @$some )?
650                         panic!("Expected value for route '{}'", $path)
651                     )?
652                 }
653                 Ok(result) => {
654                     $($( @$some )?
655                         if result.value != $route {
656                             panic!(
657                                 "Wrong value for route '{}'. Expected '{}', found '{}')",
658                                 $path, result.value, $route
659                             );
660                         }
661 
662                         let expected_params = vec![$(($key, $val)),*];
663                         let got_params = result.params.iter().collect::<Vec<_>>();
664 
665                         assert_eq!(
666                             got_params, expected_params,
667                             "Wrong params for route '{}'",
668                             $path
669                         );
670 
671                         router.at_mut($path).unwrap().value.push_str("CHECKED");
672                         assert!(router.at($path).unwrap().value.contains("CHECKED"));
673 
674                         let val = router.at_mut($path).unwrap().value;
675                         *val = val.replace("CHECKED", "");
676                     )?
677 
678                     $($( @$none )?
679                         panic!(
680                             "Unexpected value for route '{}', got: {:?}",
681                             $path,
682                             result.params.iter().collect::<Vec<_>>()
683                         );
684                     )?
685                 }
686             })*
687 
688             if let Err((got, expected)) = router.check_priorities() {
689                 panic!(
690                     "priority mismatch for node: got '{}', expected '{}'",
691                     got, expected
692                 )
693             }
694         }
695    )* };
696 }
697 
698 macro_rules! insert_tests {
699     ($($name:ident {
700         $($route:literal => $res:expr),* $(,)?
701     }),* $(,)?) => { $(
702         #[test]
703         fn $name() {
704             let mut router = Router::new();
705 
706             $(
707                 let res = router.insert($route, $route.to_owned());
708                 assert_eq!(res, $res, "unexpected result for path '{}'", $route);
709             )*
710         }
711    )* };
712 }
713 
714 macro_rules! tsr_tests {
715     ($($name:ident {
716         routes = $routes:expr,
717         $($path:literal => $tsr:ident),* $(,)?
718     }),* $(,)?) => { $(
719         #[test]
720         fn $name() {
721             let mut router = Router::new();
722 
723             for route in $routes {
724                 router.insert(route, route.to_owned())
725                     .unwrap_or_else(|e| panic!("error when inserting route '{}': {:?}", route, e));
726             }
727 
728             $(
729                 match router.at($path) {
730                     Err(MatchError::$tsr) => {},
731                     Err(e) => panic!("wrong tsr value for '{}', expected {}, found {}", $path, MatchError::$tsr, e),
732                     res => panic!("unexpected result for '{}': {:?}", $path, res)
733                 }
734             )*
735         }
736    )* };
737 }
738 
739 pub(self) use {insert_tests, match_tests, tsr_tests};
740