1use super::tree::Tree;
2use super::{IntoPattern, Resource, ResourceDef, ResourcePath};
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct ResourceId(u16);
10
11impl ResourceId {
12 pub const fn get(self) -> u16 {
14 self.0
15 }
16}
17
18#[derive(Debug, Clone)]
24pub struct Router<T, U = ()> {
25 tree: Tree,
26 resources: Vec<RouterEntry<T, U>>,
27 insensitive: bool,
28}
29
30impl<T, U> Router<T, U> {
31 pub fn builder() -> RouterBuilder<T, U> {
33 RouterBuilder {
34 resources: Vec::new(),
35 insensitive: false,
36 }
37 }
38
39 pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
44 where
45 R: Resource<P>,
46 P: ResourcePath,
47 {
48 if let Some(idx) = if self.insensitive {
49 self.tree.find_insensitive(resource)
50 } else {
51 self.tree.find(resource)
52 } {
53 let item = &self.resources[idx];
54 Some((&item.value, ResourceId(item.rdef.id())))
55 } else {
56 None
57 }
58 }
59
60 pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>
62 where
63 R: Resource<P>,
64 P: ResourcePath,
65 {
66 if let Some(idx) = if self.insensitive {
67 self.tree.find_insensitive(resource)
68 } else {
69 self.tree.find(resource)
70 } {
71 let item = &mut self.resources[idx];
72 Some((&mut item.value, ResourceId(item.rdef.id())))
73 } else {
74 None
75 }
76 }
77
78 pub fn recognize_checked<R, P, F>(&self, resource: &mut R, check: F) -> Option<(&T, ResourceId)>
83 where
84 F: Fn(&R, Option<&U>) -> bool,
85 R: Resource<P>,
86 P: ResourcePath,
87 {
88 if let Some(idx) = if self.insensitive {
89 self.tree.find_checked_insensitive(resource, &|idx, res| {
90 let item = &self.resources[idx];
91 check(res, item.check.as_ref())
92 })
93 } else {
94 self.tree.find_checked(resource, &|idx, res| {
95 let item = &self.resources[idx];
96 check(res, item.check.as_ref())
97 })
98 } {
99 let item = &self.resources[idx];
100 Some((&item.value, ResourceId(item.rdef.id())))
101 } else {
102 None
103 }
104 }
105
106 pub fn recognize_checked_mut<R, P, F>(
109 &mut self,
110 resource: &mut R,
111 check: F,
112 ) -> Option<(&mut T, ResourceId)>
113 where
114 F: Fn(&R, Option<&U>) -> bool,
115 R: Resource<P>,
116 P: ResourcePath,
117 {
118 if let Some(idx) = if self.insensitive {
119 self.tree.find_checked_insensitive(resource, &|idx, res| {
120 let item = &self.resources[idx];
121 check(res, item.check.as_ref())
122 })
123 } else {
124 self.tree.find_checked(resource, &|idx, res| {
125 let item = &self.resources[idx];
126 check(res, item.check.as_ref())
127 })
128 } {
129 let item = &mut self.resources[idx];
130 Some((&mut item.value, ResourceId(item.rdef.id())))
131 } else {
132 None
133 }
134 }
135}
136
137#[derive(Debug, Clone)]
142pub struct RouterEntry<T, U = ()> {
143 rdef: ResourceDef,
144 value: T,
145 check: Option<U>,
146}
147
148impl<T, U> RouterEntry<T, U> {
149 pub fn resource(&self) -> &ResourceDef {
151 &self.rdef
152 }
153
154 pub fn resource_mut(&mut self) -> &mut ResourceDef {
156 &mut self.rdef
157 }
158
159 pub fn value(&self) -> &T {
161 &self.value
162 }
163
164 pub fn value_mut(&mut self) -> &mut T {
166 &mut self.value
167 }
168
169 pub fn check_value(&self) -> Option<&U> {
172 self.check.as_ref()
173 }
174
175 pub fn set_check_value<V: Into<Option<U>>>(&mut self, value: V) -> &mut Self {
178 self.check = value.into();
179 self
180 }
181
182 pub fn set_id(&mut self, id: u16) -> &mut Self {
184 self.rdef.set_id(id);
185 self
186 }
187
188 pub fn set_name<N: Into<String>>(&mut self, name: N) -> &mut Self {
190 self.rdef.set_name(name);
191 self
192 }
193}
194
195#[derive(Debug)]
196pub struct RouterBuilder<T, U = ()> {
202 insensitive: bool,
203 resources: Vec<RouterEntry<T, U>>,
204}
205
206impl<T, U> RouterBuilder<T, U> {
207 pub fn case_insensitive(&mut self) {
212 self.insensitive = true;
213 }
214
215 pub fn path<P: IntoPattern>(&mut self, path: P, resource: T) -> &mut RouterEntry<T, U> {
218 self.resource(ResourceDef::new(path), resource)
219 }
220
221 pub fn prefix<P: IntoPattern>(&mut self, prefix: P, resource: T) -> &mut RouterEntry<T, U> {
224 self.resource(ResourceDef::prefix(prefix), resource)
225 }
226
227 pub fn resource(&mut self, rdef: ResourceDef, resource: T) -> &mut RouterEntry<T, U> {
229 self.resources.push(RouterEntry {
230 rdef,
231 value: resource,
232 check: None,
233 });
234 self.resources.last_mut().unwrap()
235 }
236
237 pub fn build(self) -> Router<T, U> {
239 let tree = if self.resources.is_empty() {
240 Tree::default()
241 } else {
242 let mut tree = Tree::new(&self.resources[0].rdef, 0);
243 for (idx, r) in self.resources[1..].iter().enumerate() {
244 tree.insert(&r.rdef, idx + 1);
245 }
246 tree
247 };
248
249 Router {
250 tree,
251 resources: self.resources,
252 insensitive: self.insensitive,
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use crate::path::Path;
260 use crate::router::{ResourceId, Router};
261
262 #[test]
263 fn test_recognizer_1() {
264 let mut router = Router::<usize>::builder();
265 router.path("/name", 10).set_id(0);
266 router.path("/name/{val}", 11).set_id(1);
267 router.path("/name/{val}/index.html", 12).set_id(2);
268 router.path("/file/{file}.{ext}", 13).set_id(3);
269 router.path("/v{val}/{val2}/index.html", 14).set_id(4);
270 router.path("/v/{tail}*", 15).set_id(5);
271 router.path("/test2/{test}.html", 16).set_id(6);
272 router.path("/{test}/index.html", 17).set_id(7);
273 router.path("/v2/{custom:.*}/test.html", 18).set_id(8);
274 let mut router = router.build();
275
276 let mut path = Path::new("/unknown");
277 assert!(router.recognize_mut(&mut path).is_none());
278
279 let mut path = Path::new("/unknown");
280 assert!(router.recognize(&mut path).is_none());
281
282 let mut path = Path::new("/name");
283 let (h, info) = router.recognize_mut(&mut path).unwrap();
284 assert_eq!(*h, 10);
285 assert_eq!(info, ResourceId(0));
286 assert!(path.is_empty());
287
288 let mut path = Path::new("/name");
289 let (h, info) = router.recognize(&mut path).unwrap();
290 assert_eq!(*h, 10);
291 assert_eq!(info, ResourceId(0));
292 assert!(path.is_empty());
293
294 let mut path = Path::new("/name/value");
295 let (h, info) = router.recognize_mut(&mut path).unwrap();
296 assert_eq!(*h, 11);
297 assert_eq!(info, ResourceId(1));
298 assert_eq!(path.get("val").unwrap(), "value");
299 assert_eq!(&path["val"], "value");
300
301 let mut path = Path::new("/name/value2/index.html");
302 let (h, info) = router.recognize_mut(&mut path).unwrap();
303 assert_eq!(*h, 12);
304 assert_eq!(info, ResourceId(2));
305 assert_eq!(path.get("val").unwrap(), "value2");
306
307 let mut path = Path::new("/file/file.gz");
308 let (h, info) = router.recognize_mut(&mut path).unwrap();
309 assert_eq!(*h, 13);
310 assert_eq!(info, ResourceId(3));
311 assert_eq!(path.get("file").unwrap(), "file");
312 assert_eq!(path.get("ext").unwrap(), "gz");
313
314 let mut path = Path::new("/vtest/ttt/index.html");
315 let (h, info) = router.recognize_mut(&mut path).unwrap();
316 assert_eq!(*h, 14);
317 assert_eq!(info, ResourceId(4));
318 assert_eq!(path.get("val").unwrap(), "test");
319 assert_eq!(path.get("val2").unwrap(), "ttt");
320
321 let mut path = Path::new("/v/blah-blah/index.html");
322 let (h, info) = router.recognize_mut(&mut path).unwrap();
323 assert_eq!(*h, 15);
324 assert_eq!(info, ResourceId(5));
325 assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
326
327 let mut path = Path::new("/test2/index.html");
328 let (h, info) = router.recognize_mut(&mut path).unwrap();
329 assert_eq!(*h, 16);
330 assert_eq!(info, ResourceId(6));
331 assert_eq!(path.get("test").unwrap(), "index");
332
333 let mut path = Path::new("/bbb/index.html");
334 let (h, info) = router.recognize_mut(&mut path).unwrap();
335 assert_eq!(*h, 17);
336 assert_eq!(info, ResourceId(7));
337 assert_eq!(path.get("test").unwrap(), "bbb");
338
339 let mut path = Path::new("/v2/blah-blah/test.html");
340 let (h, info) = router.recognize_mut(&mut path).unwrap();
341 assert_eq!(*h, 18);
342 assert_eq!(info, ResourceId(8));
343 assert_eq!(path.get("custom").unwrap(), "blah-blah");
344 }
345
346 #[test]
347 fn test_recognizer_2() {
348 let mut router = Router::<usize>::builder();
349 router.path("/index.json", 10);
350 router.path("/{source}.json", 11);
351 let mut router = router.build();
352
353 let mut path = Path::new("/index.json");
354 let (h, _) = router.recognize_mut(&mut path).unwrap();
355 assert_eq!(*h, 10);
356
357 let mut path = Path::new("/test.json");
358 let (h, _) = router.recognize_mut(&mut path).unwrap();
359 assert_eq!(*h, 11);
360 }
361
362 #[test]
363 fn test_recognizer_3() {
364 let mut router = Router::<usize>::builder();
365 router.path("/index.json", 10);
366 router.path("/{source}.json", 11);
367 router.case_insensitive();
368 let mut router = router.build();
369
370 let mut path = Path::new("/index.json");
371 let (h, _) = router.recognize_mut(&mut path).unwrap();
372 assert_eq!(*h, 10);
373
374 let mut path = Path::new("/indeX.json");
375 let (h, _) = router.recognize_mut(&mut path).unwrap();
376 assert_eq!(*h, 10);
377
378 let mut path = Path::new("/test.jsoN");
379 assert!(router.recognize_mut(&mut path).is_none());
380 }
381
382 #[test]
383 fn test_recognizer_long_path() {
384 let mut router = Router::<usize>::builder();
385 router.path("/{name}/{id}/{tail}*", 10);
386 let router = router.build();
387
388 let name = "a".repeat(u16::MAX as usize + 10);
389 let mut path = Path::new(format!("/{name}/1/test/tail"));
390 assert_eq!(router.recognize(&mut path), Some((&10, ResourceId(0))));
391 assert_eq!(path.get("name"), Some(name.as_str()));
392 assert_eq!(path.get("id"), Some("1"));
393 assert_eq!(path.get("tail"), Some("test/tail"));
394
395 let mut router = Router::<usize>::builder();
396 router.prefix("/prefix", 10);
397 let router = router.build();
398
399 let mut path = Path::new(format!("/prefix/{name}"));
400 path.skip(u32::from(u16::MAX) + 1);
401 assert_eq!(router.recognize(&mut path), None);
402
403 let mut path = Path::new(format!("/{name}/prefix/test"));
404 path.skip(name.len() as u32 + 1);
405 assert_eq!(router.recognize(&mut path), Some((&10, ResourceId(0))));
406 assert_eq!(path.path(), "/test");
407 }
408
409 #[test]
410 fn test_recognizer_with_path_skip() {
411 let mut router = Router::<usize>::builder();
412 router.path("/name", 10).set_id(0);
413 router.path("/name/{val}", 11).set_id(1);
414 let mut router = router.build();
415
416 let mut path = Path::new("/name");
417 path.skip(5);
418 assert!(router.recognize_mut(&mut path).is_none());
419
420 let mut path = Path::new("/test/name");
421 path.skip(5);
422 let (h, _) = router.recognize_mut(&mut path).unwrap();
423 assert_eq!(*h, 10);
424
425 let mut path = Path::new("/test/name/value");
426 path.skip(5);
427 let (h, id) = router.recognize_mut(&mut path).unwrap();
428 assert_eq!(*h, 11);
429 assert_eq!(id, ResourceId(1));
430 assert_eq!(path.get("val").unwrap(), "value");
431 assert_eq!(&path["val"], "value");
432
433 let mut router = Router::<usize>::builder();
435 router.path("/name", 10);
436 router.path("/name/{val}", 11);
437 let mut router = router.build();
438
439 let mut path = Path::new("/name");
440 path.skip(5);
441 assert!(router.recognize_mut(&mut path).is_none());
442
443 let mut path = Path::new("/test2/name");
444 path.skip(6);
445 let (h, _) = router.recognize_mut(&mut path).unwrap();
446 assert_eq!(*h, 10);
447
448 let mut path = Path::new("/test2/name-test");
449 path.skip(6);
450 assert!(router.recognize_mut(&mut path).is_none());
451
452 let mut path = Path::new("/test2/name/ttt");
453 path.skip(6);
454 let (h, _) = router.recognize_mut(&mut path).unwrap();
455 assert_eq!(*h, 11);
456 assert_eq!(&path["val"], "ttt");
457 }
458
459 #[test]
460 fn test_recognizer_checked() {
461 let mut router = Router::<usize, usize>::builder();
462 router.path("/name", 10).set_check_value(0);
463 router.path("/name", 11).set_check_value(1);
464 router.path("/name", 12).set_check_value(2);
465 let mut router = router.build();
466
467 let mut p = Path::new("/name");
468 assert_eq!(
469 *router
470 .recognize_checked(&mut p, |_, v| v == Some(&0))
471 .unwrap()
472 .0,
473 10
474 );
475 let mut p = Path::new("/name");
476 assert_eq!(
477 *router
478 .recognize_checked(&mut p, |_, v| v == Some(&1))
479 .unwrap()
480 .0,
481 11
482 );
483 let mut p = Path::new("/name");
484 assert_eq!(
485 *router
486 .recognize_checked(&mut p, |_, v| v == Some(&2))
487 .unwrap()
488 .0,
489 12
490 );
491 let mut p = Path::new("/name");
492 assert_eq!(
493 *router
494 .recognize_checked_mut(&mut p, |_, v| v == Some(&0))
495 .unwrap()
496 .0,
497 10
498 );
499 let mut p = Path::new("/name");
500 assert_eq!(
501 *router
502 .recognize_checked_mut(&mut p, |_, v| v == Some(&1))
503 .unwrap()
504 .0,
505 11
506 );
507 let mut p = Path::new("/name");
508 assert_eq!(
509 *router
510 .recognize_checked_mut(&mut p, |_, v| v == Some(&2))
511 .unwrap()
512 .0,
513 12
514 );
515 }
516
517 #[test]
518 fn test_recognizer_checked_insensitive() {
519 let mut router = Router::<usize, usize>::builder();
520 router.case_insensitive();
521 router.path("/name", 10).set_check_value(0);
522 router.path("/name", 11).set_check_value(1);
523 router.path("/name", 12).set_check_value(2);
524 let mut router = router.build();
525
526 let mut p = Path::new("/Name");
527 assert_eq!(
528 *router
529 .recognize_checked(&mut p, |_, v| v == Some(&0))
530 .unwrap()
531 .0,
532 10
533 );
534 let mut p = Path::new("/Name");
535 assert_eq!(
536 *router
537 .recognize_checked(&mut p, |_, v| v == Some(&1))
538 .unwrap()
539 .0,
540 11
541 );
542 let mut p = Path::new("/Name");
543 assert_eq!(
544 *router
545 .recognize_checked_mut(&mut p, |_, v| v == Some(&0))
546 .unwrap()
547 .0,
548 10
549 );
550 let mut p = Path::new("/name");
551 assert_eq!(
552 *router
553 .recognize_checked_mut(&mut p, |_, v| v == Some(&1))
554 .unwrap()
555 .0,
556 11
557 );
558 }
559
560 #[test]
561 fn test_prefix_exact_match_tail() {
562 for (prefix, p, tail) in [
563 ("/app", "/app", ""),
564 ("/app", "/app/", "/"),
565 ("/app", "/app/test", "/test"),
566 ("/{name}", "/x", ""),
567 ("/a/{name}", "/a/x", ""),
568 ] {
569 let mut router = Router::<usize>::builder();
570 router.prefix(prefix, 1);
571 let router = router.build();
572
573 let mut path = Path::new(p);
574 assert!(router.recognize(&mut path).is_some());
575 assert_eq!(path.path(), tail);
576 assert_eq!(path.path(), tail);
577 assert_eq!(path.get("tail"), Some(tail));
578 assert_eq!(&path["tail"], tail);
579 }
580
581 let mut path = Path::new("/app");
582 path.skip(10);
583 assert_eq!(path.path(), "");
584 assert_eq!(path.get("tail"), Some(""));
585 }
586}