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