Skip to main content

ntex_router/
router.rs

1use super::tree::Tree;
2use super::{IntoPattern, Resource, ResourceDef, ResourcePath};
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5/// Id of a matched resource.
6///
7/// It is the id set with [`ResourceDef::set_id()`], resources without an
8/// explicitly set id all have id `0`.
9pub struct ResourceId(u16);
10
11impl ResourceId {
12    /// Numeric value of the id.
13    pub const fn get(self) -> u16 {
14        self.0
15    }
16}
17
18/// Resource router.
19///
20/// Maps paths to values of type `T`. Each resource can also have an optional
21/// value of type `U` used by the `recognize_*checked` methods. Resources are
22/// matched in registration order, the first matching resource wins.
23#[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    /// Creates a router builder.
32    pub fn builder() -> RouterBuilder<T, U> {
33        RouterBuilder {
34            resources: Vec::new(),
35            insensitive: false,
36        }
37    }
38
39    /// Finds the first resource that matches the path.
40    ///
41    /// On a match, the values of dynamic segments are stored in `resource`,
42    /// and a prefix match skips the matched part of the path.
43    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    /// Same as [`recognize()`](Self::recognize), returns a mutable reference.
61    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    /// Finds the first resource that matches the path and passes `check`.
79    ///
80    /// `check` is called for each matching resource with the resource and the
81    /// resource's optional value of type `U`.
82    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    /// Same as [`recognize_checked()`](Self::recognize_checked), returns a
107    /// mutable reference.
108    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/// Registered resource, see [`RouterBuilder`].
138///
139/// Holds the resource definition, the value and the optional value passed to
140/// the `check` function of the `recognize_*checked` methods.
141#[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    /// Resource definition
150    pub fn resource(&self) -> &ResourceDef {
151        &self.rdef
152    }
153
154    /// Mutable reference to the resource definition
155    pub fn resource_mut(&mut self) -> &mut ResourceDef {
156        &mut self.rdef
157    }
158
159    /// Value returned for matched resource
160    pub fn value(&self) -> &T {
161        &self.value
162    }
163
164    /// Mutable reference to the value returned for matched resource
165    pub fn value_mut(&mut self) -> &mut T {
166        &mut self.value
167    }
168
169    /// Value passed to the `check` function of the `recognize_*checked`
170    /// methods
171    pub fn check_value(&self) -> Option<&U> {
172        self.check.as_ref()
173    }
174
175    /// Set value passed to the `check` function of the `recognize_*checked`
176    /// methods
177    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    /// Set resource id, see [`ResourceDef::set_id()`]
183    pub fn set_id(&mut self, id: u16) -> &mut Self {
184        self.rdef.set_id(id);
185        self
186    }
187
188    /// Set resource name, see [`ResourceDef::set_name()`]
189    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)]
196/// Router builder, see [`Router::builder()`].
197///
198/// The registration methods return the registered [`RouterEntry`], it can be
199/// used to set the resource id or name, or the value used by the
200/// `recognize_*checked` methods.
201pub struct RouterBuilder<T, U = ()> {
202    insensitive: bool,
203    resources: Vec<RouterEntry<T, U>>,
204}
205
206impl<T, U> RouterBuilder<T, U> {
207    /// Make router case insensitive. Only static segments
208    /// could be case insensitive.
209    ///
210    /// By default router is case sensitive.
211    pub fn case_insensitive(&mut self) {
212        self.insensitive = true;
213    }
214
215    /// Register resource for specified path patterns, see
216    /// [`ResourceDef::new()`].
217    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    /// Register resource for specified path prefix patterns, see
222    /// [`ResourceDef::prefix()`].
223    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    /// Register resource for `ResourceDef`
228    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    /// Finish configuration and create router instance.
238    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        // same patterns
434        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}