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)]
5pub struct ResourceId(u16);
6
7/// Resource router.
8#[derive(Debug, Clone)]
9pub struct Router<T, U = ()> {
10    tree: Tree,
11    resources: Vec<(ResourceDef, T, Option<U>)>,
12    insensitive: bool,
13}
14
15impl<T, U> Router<T, U> {
16    pub fn build() -> RouterBuilder<T, U> {
17        RouterBuilder {
18            resources: Vec::new(),
19            insensitive: false,
20        }
21    }
22
23    pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
24    where
25        R: Resource<P>,
26        P: ResourcePath,
27    {
28        if let Some(idx) = if self.insensitive {
29            self.tree.find_insensitive(resource)
30        } else {
31            self.tree.find(resource)
32        } {
33            let item = &self.resources[idx];
34            Some((&item.1, ResourceId(item.0.id())))
35        } else {
36            None
37        }
38    }
39
40    pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>
41    where
42        R: Resource<P>,
43        P: ResourcePath,
44    {
45        if let Some(idx) = if self.insensitive {
46            self.tree.find_insensitive(resource)
47        } else {
48            self.tree.find(resource)
49        } {
50            let item = &mut self.resources[idx];
51            Some((&mut item.1, ResourceId(item.0.id())))
52        } else {
53            None
54        }
55    }
56
57    pub fn recognize_checked<R, P, F>(
58        &self,
59        resource: &mut R,
60        check: F,
61    ) -> Option<(&T, ResourceId)>
62    where
63        F: Fn(&R, Option<&U>) -> bool,
64        R: Resource<P>,
65        P: ResourcePath,
66    {
67        if let Some(idx) = if self.insensitive {
68            self.tree.find_checked_insensitive(resource, &|idx, res| {
69                let item = &self.resources[idx];
70                check(res, item.2.as_ref())
71            })
72        } else {
73            self.tree.find_checked(resource, &|idx, res| {
74                let item = &self.resources[idx];
75                check(res, item.2.as_ref())
76            })
77        } {
78            let item = &self.resources[idx];
79            Some((&item.1, ResourceId(item.0.id())))
80        } else {
81            None
82        }
83    }
84
85    pub fn recognize_mut_checked<R, P, F>(
86        &mut self,
87        resource: &mut R,
88        check: F,
89    ) -> Option<(&mut T, ResourceId)>
90    where
91        F: Fn(&R, Option<&U>) -> bool,
92        R: Resource<P>,
93        P: ResourcePath,
94    {
95        if let Some(idx) = if self.insensitive {
96            self.tree.find_checked_insensitive(resource, &|idx, res| {
97                let item = &self.resources[idx];
98                check(res, item.2.as_ref())
99            })
100        } else {
101            self.tree.find_checked(resource, &|idx, res| {
102                let item = &self.resources[idx];
103                check(res, item.2.as_ref())
104            })
105        } {
106            let item = &mut self.resources[idx];
107            Some((&mut item.1, ResourceId(item.0.id())))
108        } else {
109            None
110        }
111    }
112}
113
114#[derive(Debug)]
115pub struct RouterBuilder<T, U = ()> {
116    insensitive: bool,
117    resources: Vec<(ResourceDef, T, Option<U>)>,
118}
119
120impl<T, U> RouterBuilder<T, U> {
121    /// Make router case insensitive. Only static segments
122    /// could be case insensitive.
123    ///
124    /// By default router is case sensitive.
125    pub fn case_insensitive(&mut self) {
126        self.insensitive = true;
127    }
128
129    /// Register resource for specified path.
130    pub fn path<P: IntoPattern>(
131        &mut self,
132        path: P,
133        resource: T,
134    ) -> &mut (ResourceDef, T, Option<U>) {
135        self.resources
136            .push((ResourceDef::new(path), resource, None));
137        self.resources.last_mut().unwrap()
138    }
139
140    /// Register resource for specified path prefix.
141    pub fn prefix(
142        &mut self,
143        prefix: &str,
144        resource: T,
145    ) -> &mut (ResourceDef, T, Option<U>) {
146        self.resources
147            .push((ResourceDef::prefix(prefix), resource, None));
148        self.resources.last_mut().unwrap()
149    }
150
151    /// Register resource for ResourceDef
152    pub fn rdef(
153        &mut self,
154        rdef: ResourceDef,
155        resource: T,
156    ) -> &mut (ResourceDef, T, Option<U>) {
157        self.resources.push((rdef, resource, None));
158        self.resources.last_mut().unwrap()
159    }
160
161    /// Finish configuration and create router instance.
162    pub fn finish(self) -> Router<T, U> {
163        let tree = if self.resources.is_empty() {
164            Tree::default()
165        } else {
166            let mut tree = Tree::new(&self.resources[0].0, 0);
167            for (idx, r) in self.resources[1..].iter().enumerate() {
168                tree.insert(&r.0, idx + 1)
169            }
170            tree
171        };
172
173        Router {
174            tree,
175            resources: self.resources,
176            insensitive: self.insensitive,
177        }
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use crate::path::Path;
184    use crate::router::{ResourceId, Router};
185
186    #[test]
187    fn test_recognizer_1() {
188        let mut router = Router::<usize>::build();
189        router.path("/name", 10).0.set_id(0);
190        router.path("/name/{val}", 11).0.set_id(1);
191        router.path("/name/{val}/index.html", 12).0.set_id(2);
192        router.path("/file/{file}.{ext}", 13).0.set_id(3);
193        router.path("/v{val}/{val2}/index.html", 14).0.set_id(4);
194        router.path("/v/{tail}*", 15).0.set_id(5);
195        router.path("/test2/{test}.html", 16).0.set_id(6);
196        router.path("/{test}/index.html", 17).0.set_id(7);
197        router.path("/v2/{custom:.*}/test.html", 18).0.set_id(8);
198        let mut router = router.finish();
199
200        let mut path = Path::new("/unknown");
201        assert!(router.recognize_mut(&mut path).is_none());
202
203        let mut path = Path::new("/unknown");
204        assert!(router.recognize(&mut path).is_none());
205
206        let mut path = Path::new("/name");
207        let (h, info) = router.recognize_mut(&mut path).unwrap();
208        assert_eq!(*h, 10);
209        assert_eq!(info, ResourceId(0));
210        assert!(path.is_empty());
211
212        let mut path = Path::new("/name");
213        let (h, info) = router.recognize(&mut path).unwrap();
214        assert_eq!(*h, 10);
215        assert_eq!(info, ResourceId(0));
216        assert!(path.is_empty());
217
218        let mut path = Path::new("/name/value");
219        let (h, info) = router.recognize_mut(&mut path).unwrap();
220        assert_eq!(*h, 11);
221        assert_eq!(info, ResourceId(1));
222        assert_eq!(path.get("val").unwrap(), "value");
223        assert_eq!(&path["val"], "value");
224
225        let mut path = Path::new("/name/value2/index.html");
226        let (h, info) = router.recognize_mut(&mut path).unwrap();
227        assert_eq!(*h, 12);
228        assert_eq!(info, ResourceId(2));
229        assert_eq!(path.get("val").unwrap(), "value2");
230
231        let mut path = Path::new("/file/file.gz");
232        let (h, info) = router.recognize_mut(&mut path).unwrap();
233        assert_eq!(*h, 13);
234        assert_eq!(info, ResourceId(3));
235        assert_eq!(path.get("file").unwrap(), "file");
236        assert_eq!(path.get("ext").unwrap(), "gz");
237
238        let mut path = Path::new("/vtest/ttt/index.html");
239        let (h, info) = router.recognize_mut(&mut path).unwrap();
240        assert_eq!(*h, 14);
241        assert_eq!(info, ResourceId(4));
242        assert_eq!(path.get("val").unwrap(), "test");
243        assert_eq!(path.get("val2").unwrap(), "ttt");
244
245        let mut path = Path::new("/v/blah-blah/index.html");
246        let (h, info) = router.recognize_mut(&mut path).unwrap();
247        assert_eq!(*h, 15);
248        assert_eq!(info, ResourceId(5));
249        assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
250
251        let mut path = Path::new("/test2/index.html");
252        let (h, info) = router.recognize_mut(&mut path).unwrap();
253        assert_eq!(*h, 16);
254        assert_eq!(info, ResourceId(6));
255        assert_eq!(path.get("test").unwrap(), "index");
256
257        let mut path = Path::new("/bbb/index.html");
258        let (h, info) = router.recognize_mut(&mut path).unwrap();
259        assert_eq!(*h, 17);
260        assert_eq!(info, ResourceId(7));
261        assert_eq!(path.get("test").unwrap(), "bbb");
262
263        let mut path = Path::new("/v2/blah-blah/test.html");
264        let (h, info) = router.recognize_mut(&mut path).unwrap();
265        assert_eq!(*h, 18);
266        assert_eq!(info, ResourceId(8));
267        assert_eq!(path.get("custom").unwrap(), "blah-blah");
268    }
269
270    #[test]
271    fn test_recognizer_2() {
272        let mut router = Router::<usize>::build();
273        router.path("/index.json", 10);
274        router.path("/{source}.json", 11);
275        let mut router = router.finish();
276
277        let mut path = Path::new("/index.json");
278        let (h, _) = router.recognize_mut(&mut path).unwrap();
279        assert_eq!(*h, 10);
280
281        let mut path = Path::new("/test.json");
282        let (h, _) = router.recognize_mut(&mut path).unwrap();
283        assert_eq!(*h, 11);
284    }
285
286    #[test]
287    fn test_recognizer_3() {
288        let mut router = Router::<usize>::build();
289        router.path("/index.json", 10);
290        router.path("/{source}.json", 11);
291        router.case_insensitive();
292        let mut router = router.finish();
293
294        let mut path = Path::new("/index.json");
295        let (h, _) = router.recognize_mut(&mut path).unwrap();
296        assert_eq!(*h, 10);
297
298        let mut path = Path::new("/indeX.json");
299        let (h, _) = router.recognize_mut(&mut path).unwrap();
300        assert_eq!(*h, 10);
301
302        let mut path = Path::new("/test.jsoN");
303        assert!(router.recognize_mut(&mut path).is_none());
304    }
305
306    #[test]
307    fn test_recognizer_with_path_skip() {
308        let mut router = Router::<usize>::build();
309        router.path("/name", 10).0.set_id(0);
310        router.path("/name/{val}", 11).0.set_id(1);
311        let mut router = router.finish();
312
313        let mut path = Path::new("/name");
314        path.skip(5);
315        assert!(router.recognize_mut(&mut path).is_none());
316
317        let mut path = Path::new("/test/name");
318        path.skip(5);
319        let (h, _) = router.recognize_mut(&mut path).unwrap();
320        assert_eq!(*h, 10);
321
322        let mut path = Path::new("/test/name/value");
323        path.skip(5);
324        let (h, id) = router.recognize_mut(&mut path).unwrap();
325        assert_eq!(*h, 11);
326        assert_eq!(id, ResourceId(1));
327        assert_eq!(path.get("val").unwrap(), "value");
328        assert_eq!(&path["val"], "value");
329
330        // same patterns
331        let mut router = Router::<usize>::build();
332        router.path("/name", 10);
333        router.path("/name/{val}", 11);
334        let mut router = router.finish();
335
336        let mut path = Path::new("/name");
337        path.skip(5);
338        assert!(router.recognize_mut(&mut path).is_none());
339
340        let mut path = Path::new("/test2/name");
341        path.skip(6);
342        let (h, _) = router.recognize_mut(&mut path).unwrap();
343        assert_eq!(*h, 10);
344
345        let mut path = Path::new("/test2/name-test");
346        path.skip(6);
347        assert!(router.recognize_mut(&mut path).is_none());
348
349        let mut path = Path::new("/test2/name/ttt");
350        path.skip(6);
351        let (h, _) = router.recognize_mut(&mut path).unwrap();
352        assert_eq!(*h, 11);
353        assert_eq!(&path["val"], "ttt");
354    }
355
356    #[test]
357    fn test_recognizer_checked() {
358        let mut router = Router::<usize, usize>::build();
359        router.path("/name", 10).2 = Some(0);
360        router.path("/name", 11).2 = Some(1);
361        router.path("/name", 12).2 = Some(2);
362        let mut router = router.finish();
363
364        let mut p = Path::new("/name");
365        assert_eq!(
366            *router
367                .recognize_checked(&mut p, |_, v| v == Some(&0))
368                .unwrap()
369                .0,
370            10
371        );
372        let mut p = Path::new("/name");
373        assert_eq!(
374            *router
375                .recognize_checked(&mut p, |_, v| v == Some(&1))
376                .unwrap()
377                .0,
378            11
379        );
380        let mut p = Path::new("/name");
381        assert_eq!(
382            *router
383                .recognize_checked(&mut p, |_, v| v == Some(&2))
384                .unwrap()
385                .0,
386            12
387        );
388        let mut p = Path::new("/name");
389        assert_eq!(
390            *router
391                .recognize_mut_checked(&mut p, |_, v| v == Some(&0))
392                .unwrap()
393                .0,
394            10
395        );
396        let mut p = Path::new("/name");
397        assert_eq!(
398            *router
399                .recognize_mut_checked(&mut p, |_, v| v == Some(&1))
400                .unwrap()
401                .0,
402            11
403        );
404        let mut p = Path::new("/name");
405        assert_eq!(
406            *router
407                .recognize_mut_checked(&mut p, |_, v| v == Some(&2))
408                .unwrap()
409                .0,
410            12
411        );
412    }
413
414    #[test]
415    fn test_recognizer_checked_insensitive() {
416        let mut router = Router::<usize, usize>::build();
417        router.case_insensitive();
418        router.path("/name", 10).2 = Some(0);
419        router.path("/name", 11).2 = Some(1);
420        router.path("/name", 12).2 = Some(2);
421        let mut router = router.finish();
422
423        let mut p = Path::new("/Name");
424        assert_eq!(
425            *router
426                .recognize_checked(&mut p, |_, v| v == Some(&0))
427                .unwrap()
428                .0,
429            10
430        );
431        let mut p = Path::new("/Name");
432        assert_eq!(
433            *router
434                .recognize_checked(&mut p, |_, v| v == Some(&1))
435                .unwrap()
436                .0,
437            11
438        );
439        let mut p = Path::new("/Name");
440        assert_eq!(
441            *router
442                .recognize_mut_checked(&mut p, |_, v| v == Some(&0))
443                .unwrap()
444                .0,
445            10
446        );
447        let mut p = Path::new("/name");
448        assert_eq!(
449            *router
450                .recognize_mut_checked(&mut p, |_, v| v == Some(&1))
451                .unwrap()
452                .0,
453            11
454        );
455    }
456}