Skip to main content

ntex_router/
resource.rs

1#![allow(clippy::needless_pass_by_value)]
2use std::collections::HashMap;
3use std::hash::{Hash, Hasher};
4
5use regex::{Regex, escape};
6
7use super::IntoPattern;
8
9#[derive(Clone, Debug)]
10pub(super) struct Segments {
11    pub(super) tp: Vec<Segment>,
12    pub(super) slesh: bool,
13}
14
15/// `ResourceDef` describes an entry in resources table
16///
17/// A resource definition can have several path patterns, a path matches the
18/// resource if it matches any of them. The first pattern is the primary one,
19/// it is used by [`pattern()`](Self::pattern) and for building resource paths.
20#[derive(Clone, Debug)]
21pub struct ResourceDef {
22    id: u16,
23    pub(super) tp: Vec<Segments>, // set of matching paths
24    name: String,
25    patterns: Vec<String>,
26    elements: Vec<PathElement>,
27    pub(super) prefix: bool,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31enum PathElement {
32    Str(String),
33    Var(String),
34}
35
36impl PathElement {
37    fn is_str(&self) -> bool {
38        matches!(self, PathElement::Str(_))
39    }
40
41    fn into_str(self) -> String {
42        if let PathElement::Str(s) = self {
43            s
44        } else {
45            panic!()
46        }
47    }
48
49    fn as_str(&self) -> &str {
50        match self {
51            PathElement::Str(s) | PathElement::Var(s) => s.as_str(),
52        }
53    }
54}
55
56#[derive(Clone, Debug)]
57pub(crate) enum Segment {
58    Static(String),
59    Dynamic {
60        pattern: Regex,
61        names: Vec<&'static str>,
62        tail: bool,
63    },
64}
65
66impl Eq for Segment {}
67
68impl PartialEq for Segment {
69    fn eq(&self, other: &Self) -> bool {
70        match self {
71            Segment::Static(p1) => match other {
72                Segment::Static(p2) => p1 == p2,
73                Segment::Dynamic { .. } => false,
74            },
75            Segment::Dynamic {
76                pattern: p1,
77                tail: t1,
78                ..
79            } => match other {
80                Segment::Static { .. } => false,
81                Segment::Dynamic {
82                    pattern: p2,
83                    tail: t2,
84                    ..
85                } => p1.as_str() == p2.as_str() && t1 == t2,
86            },
87        }
88    }
89}
90
91impl ResourceDef {
92    /// Parse path patterns and create new `ResourceDef` instance.
93    ///
94    /// Path segments are separated by `/`, a leading `/` is optional. Static
95    /// segments could be case insensitive.
96    ///
97    /// Panics if path pattern is malformed.
98    pub fn new<T: IntoPattern>(path: T) -> Self {
99        ResourceDef::create(path, false)
100    }
101
102    /// Parse path patterns and create new prefix `ResourceDef` instance.
103    ///
104    /// A prefix resource matches paths that start with one of its patterns.
105    ///
106    /// Panics if path pattern is malformed.
107    pub fn prefix<T: IntoPattern>(path: T) -> Self {
108        ResourceDef::create(path, true)
109    }
110
111    /// Parse path patterns and create new prefix `ResourceDef` instance.
112    ///
113    /// Same as [`prefix()`](Self::prefix), but inserts `/` to the start of
114    /// the patterns that do not have it.
115    ///
116    /// Panics if path pattern is malformed.
117    pub fn root_prefix<T: IntoPattern>(path: T) -> Self {
118        let mut patterns = path.patterns();
119        for path in &mut patterns {
120            let p = insert_slash(path.as_str());
121            *path = p;
122        }
123
124        ResourceDef::create(patterns, true)
125    }
126
127    /// Resource id, `0` by default
128    ///
129    /// A matched resource is reported with this id, see
130    /// [`ResourceId`](crate::ResourceId).
131    pub fn id(&self) -> u16 {
132        self.id
133    }
134
135    /// Set resource id
136    pub fn set_id(&mut self, id: u16) {
137        self.id = id;
138    }
139
140    /// Check if the resource is a prefix resource, see [`prefix()`](Self::prefix)
141    pub fn is_prefix(&self) -> bool {
142        self.prefix
143    }
144
145    fn create<T: IntoPattern>(path: T, prefix: bool) -> Self {
146        let patterns = path.patterns();
147        let mut tp = Vec::with_capacity(patterns.len());
148        let mut elements = None;
149
150        for path in &patterns {
151            let (pelems, elems) = ResourceDef::parse(path);
152            tp.push(pelems);
153            // resource paths are built from the primary pattern
154            elements.get_or_insert(elems);
155        }
156
157        ResourceDef {
158            tp,
159            patterns,
160            prefix,
161            id: 0,
162            name: String::new(),
163            elements: elements.unwrap_or_default(),
164        }
165    }
166
167    /// Resource name, empty by default
168    pub fn name(&self) -> &str {
169        &self.name
170    }
171
172    /// Set resource name
173    pub fn set_name<N: Into<String>>(&mut self, name: N) {
174        self.name = name.into();
175    }
176
177    /// Primary path pattern of the resource, the first one
178    pub fn pattern(&self) -> &str {
179        self.patterns.first().map_or("", String::as_str)
180    }
181
182    /// All path patterns of the resource
183    pub fn patterns(&self) -> &[String] {
184        &self.patterns
185    }
186
187    /// Build resource path from elements, using the primary pattern.
188    ///
189    /// Elements are used for dynamic segments in pattern order, an iterator
190    /// can be passed by `&mut` to continue using it afterwards. The path is
191    /// appended to `path`. Returns `false` if there are not enough elements,
192    /// `path` is left unchanged then.
193    pub fn build_path<U, I>(&self, path: &mut String, elements: U) -> bool
194    where
195        U: IntoIterator<Item = I>,
196        I: AsRef<str>,
197    {
198        let mut elements = elements.into_iter();
199        self.build_path_with(path, |_| elements.next())
200    }
201
202    /// Build resource path from named elements, using the primary pattern.
203    ///
204    /// The path is appended to `path`. Returns `false` if an element is
205    /// missing, `path` is left unchanged then.
206    pub fn build_path_named<K, V, S>(&self, path: &mut String, elements: &HashMap<K, V, S>) -> bool
207    where
208        K: std::borrow::Borrow<str> + Eq + Hash,
209        V: AsRef<str>,
210        S: std::hash::BuildHasher,
211    {
212        self.build_path_with(path, |name| elements.get(name))
213    }
214
215    fn build_path_with<F, I>(&self, path: &mut String, mut element: F) -> bool
216    where
217        F: FnMut(&str) -> Option<I>,
218        I: AsRef<str>,
219    {
220        let len = path.len();
221        for el in &self.elements {
222            match *el {
223                PathElement::Str(ref s) => path.push_str(s),
224                PathElement::Var(ref name) => {
225                    if let Some(val) = element(name) {
226                        path.push_str(val.as_ref());
227                    } else {
228                        path.truncate(len);
229                        return false;
230                    }
231                }
232            }
233        }
234        true
235    }
236
237    fn parse_segment<'a>(
238        pattern: &'a str,
239        elems: &mut Vec<PathElement>,
240    ) -> (String, &'a str, bool) {
241        const DEFAULT_PATTERN: &str = ".+";
242        const DEFAULT_PATTERN_TAIL: &str = ".*";
243
244        let mut re = "^".to_string();
245        let mut end = None;
246        let mut tail = false;
247        let mut rem = pattern;
248        let start = usize::from(pattern.starts_with('/'));
249        let mut pattern = &pattern[start..];
250        elems.push(PathElement::Str('/'.to_string()));
251
252        while let Some(start_idx) = pattern.find('{') {
253            if let Some(end) = end
254                && start_idx > end
255            {
256                break;
257            }
258            let p = pattern.split_at(start_idx);
259            pattern = p.1;
260            re.push_str(&escape(p.0));
261            elems.push(PathElement::Str(p.0.to_string()));
262
263            // find closing }
264            let mut params_nesting = 0usize;
265            let close_idx = pattern
266                .find(|c| match c {
267                    '{' => {
268                        params_nesting += 1;
269                        false
270                    }
271                    '}' => {
272                        params_nesting -= 1;
273                        params_nesting == 0
274                    }
275                    _ => false,
276                })
277                .expect("malformed dynamic segment");
278
279            let p = pattern.split_at(close_idx + 1);
280            rem = p.1;
281            let param = &p.0[1..p.0.len() - 1]; // Remove outer brackets
282            tail = rem == "*"; // tail match (should match regardless of segments)
283
284            let (name, pat) = match param.find(':') {
285                Some(idx) => {
286                    assert!(!tail, "Custom regex is not supported for remainder match");
287                    let (name, pattern) = param.split_at(idx);
288                    (name, &pattern[1..])
289                }
290                None => (
291                    param,
292                    if tail {
293                        rem = &rem[1..];
294                        DEFAULT_PATTERN_TAIL
295                    } else {
296                        DEFAULT_PATTERN
297                    },
298                ),
299            };
300
301            re = format!(r"{}(?P<{}>{})", re, escape(name), pat);
302
303            elems.push(PathElement::Var(name.to_string()));
304
305            if let Some(idx) = rem.find(['{', '/']) {
306                end = Some(idx);
307                pattern = rem;
308            } else {
309                re.push_str(&escape(rem));
310                rem = "";
311                break;
312            }
313        }
314
315        // find end of segment
316        if let Some(idx) = rem.find('/') {
317            re.push_str(&escape(&rem[..idx]));
318            rem = &rem[idx..];
319        } else {
320            re.push_str(&escape(rem));
321            rem = "";
322        }
323        re.push('$');
324
325        (re, rem, tail)
326    }
327
328    fn parse(mut pattern: &str) -> (Segments, Vec<PathElement>) {
329        let mut elems = Vec::new();
330        let mut pelems = Vec::new();
331
332        if pattern.is_empty() {
333            return (
334                Segments {
335                    tp: Vec::new(),
336                    slesh: false,
337                },
338                Vec::new(),
339            );
340        }
341
342        loop {
343            let start = usize::from(pattern.starts_with('/'));
344            let idx = if let Some(idx) = pattern[start..].find(['{', '/']) {
345                idx + start
346            } else {
347                break;
348            };
349
350            // static segment
351            if let Some(i) = pattern[start..=idx].find('/') {
352                elems.push(PathElement::Str(pattern[..i + start].to_string()));
353                pelems.push(Segment::Static(pattern[start..i + start].to_string()));
354                pattern = &pattern[i + start..];
355                continue;
356            }
357
358            // dynamic segment
359            let (re_part, rem, tail) = Self::parse_segment(pattern, &mut elems);
360            let re = Regex::new(&re_part).unwrap();
361            let names: Vec<_> = re
362                .capture_names()
363                .filter_map(|name| name.map(|name| Box::leak(Box::new(name.to_owned())).as_str()))
364                .collect();
365            pelems.push(Segment::Dynamic {
366                names,
367                tail,
368                pattern: re,
369            });
370
371            pattern = rem;
372            if pattern.is_empty() {
373                break;
374            }
375        }
376
377        // tail
378        let slesh = pattern.ends_with('/');
379        if slesh {
380            pattern = &pattern[..pattern.len() - 1];
381        }
382        elems.push(PathElement::Str(pattern.to_string()));
383        if pattern.starts_with('/') {
384            pattern = &pattern[1..];
385        }
386        if !pattern.is_empty() {
387            // handle tail expression for static segment
388            if let Some(stripped) = pattern.strip_suffix('*') {
389                let pattern = Regex::new(&format!("^{}(.+)", escape(stripped))).unwrap();
390                pelems.push(Segment::Dynamic {
391                    pattern,
392                    names: Vec::new(),
393                    tail: true,
394                });
395            } else {
396                pelems.push(Segment::Static(pattern.to_string()));
397            }
398        }
399
400        // insert last slesh
401        if slesh {
402            elems.push(PathElement::Str("/".to_string()));
403        }
404
405        // merge path elements
406        let mut idx = 0;
407        while idx + 1 < elems.len() {
408            if elems[idx + 1].is_str() && elems[idx + 1].as_str().is_empty() {
409                elems.remove(idx + 1);
410                continue;
411            }
412            if elems[idx].is_str() && elems[idx + 1].is_str() {
413                let s2 = elems.remove(idx + 1).into_str();
414                if let PathElement::Str(ref mut s1) = elems[idx] {
415                    s1.push_str(&s2);
416                    continue;
417                }
418            }
419            idx += 1;
420        }
421
422        (Segments { tp: pelems, slesh }, elems)
423    }
424}
425
426impl Eq for ResourceDef {}
427
428impl PartialEq for ResourceDef {
429    fn eq(&self, other: &ResourceDef) -> bool {
430        self.patterns == other.patterns
431    }
432}
433
434impl Hash for ResourceDef {
435    fn hash<H: Hasher>(&self, state: &mut H) {
436        self.patterns.hash(state);
437    }
438}
439
440impl<'a> From<&'a str> for ResourceDef {
441    fn from(path: &'a str) -> ResourceDef {
442        ResourceDef::new(path)
443    }
444}
445
446impl From<String> for ResourceDef {
447    fn from(path: String) -> ResourceDef {
448        ResourceDef::new(path)
449    }
450}
451
452impl From<&String> for ResourceDef {
453    fn from(path: &String) -> ResourceDef {
454        ResourceDef::new(path)
455    }
456}
457
458impl<T: AsRef<str>> From<Vec<T>> for ResourceDef {
459    fn from(paths: Vec<T>) -> ResourceDef {
460        ResourceDef::new(paths)
461    }
462}
463
464impl<T: AsRef<str>, const N: usize> From<[T; N]> for ResourceDef {
465    fn from(paths: [T; N]) -> ResourceDef {
466        ResourceDef::new(paths)
467    }
468}
469
470pub(crate) fn insert_slash(path: &str) -> String {
471    let mut path = path.to_owned();
472    if !path.is_empty() && !path.starts_with('/') {
473        path.insert(0, '/');
474    }
475    path
476}
477
478#[cfg(test)]
479mod tests {
480    use super::*;
481    use crate::path::Path;
482    use crate::tree::Tree;
483
484    #[test]
485    fn test_parse_static() {
486        let re = ResourceDef::new("/");
487        let tree = Tree::new(&re, 1);
488        assert_eq!(tree.find(&mut Path::new("/")), Some(1));
489        assert_eq!(tree.find(&mut Path::new("/a")), None);
490
491        let re = ResourceDef::new("/name");
492        let tree = Tree::new(&re, 1);
493        assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
494        assert_eq!(tree.find(&mut Path::new("/")), None);
495        assert_eq!(tree.find(&mut Path::new("/name1")), None);
496        assert_eq!(tree.find(&mut Path::new("/name/")), None);
497        assert_eq!(tree.find(&mut Path::new("/name~")), None);
498
499        let re = ResourceDef::new("/name/");
500        let tree = Tree::new(&re, 1);
501        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
502        assert_eq!(tree.find(&mut Path::new("/name")), None);
503        assert_eq!(tree.find(&mut Path::new("/name/gs")), None);
504
505        let re = ResourceDef::new("/user/profile");
506        let tree = Tree::new(&re, 1);
507        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
508        assert_eq!(tree.find(&mut Path::new("/user/profile/profile")), None);
509
510        let mut tree = Tree::new(&ResourceDef::new("/name"), 1);
511        tree.insert(&ResourceDef::new("/name/"), 2);
512        assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
513        assert_eq!(tree.find(&mut Path::new("/name/")), Some(2));
514
515        let mut tree = Tree::new(&ResourceDef::new(""), 1);
516        tree.insert(&ResourceDef::new("/test/index.html"), 2);
517        assert_eq!(tree.find_checked(&mut Path::new(""), &|_, _| true), Some(1));
518        assert_eq!(
519            tree.find_checked(&mut Path::new("index.html"), &|_, _| true),
520            None
521        );
522        assert_eq!(
523            tree.find_checked(&mut Path::new("test/index.html"), &|_, _| true),
524            Some(2)
525        );
526    }
527
528    #[test]
529    fn test_parse_param() {
530        let tree = Tree::new(&ResourceDef::new("/{id}"), 1);
531        assert_eq!(tree.find(&mut Path::new("/profile")), Some(1));
532        assert_eq!(tree.find(&mut Path::new("/2345")), Some(1));
533        assert_eq!(tree.find(&mut Path::new("/2345/")), None);
534        assert_eq!(tree.find(&mut Path::new("/2345/sdg")), None);
535
536        let re = ResourceDef::new("/user/{id}");
537        let tree = Tree::new(&re, 1);
538        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
539        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
540        assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
541        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
542
543        let mut resource = Path::new("/user/profile");
544        let tree = Tree::new(&re, 1);
545        assert_eq!(tree.find(&mut resource), Some(1));
546        assert_eq!(resource.get("id").unwrap(), "profile");
547
548        let mut resource = Path::new("/user/1245125");
549        assert_eq!(tree.find(&mut resource), Some(1));
550        assert_eq!(resource.get("id").unwrap(), "1245125");
551
552        let tree = Tree::new(&ResourceDef::new("/v{version}/resource/{id}"), 1);
553        assert_eq!(tree.find(&mut Path::new("/v1/resource/320120")), Some(1));
554        assert_eq!(tree.find(&mut Path::new("/v1/resource/320120/")), None,);
555        assert_eq!(tree.find(&mut Path::new("/v/resource/1")), None);
556        assert_eq!(tree.find(&mut Path::new("/resource")), None);
557
558        let mut resource = Path::new("/v151/resource/adahg32");
559        assert_eq!(tree.find(&mut resource), Some(1));
560        assert_eq!(resource.get("version").unwrap(), "151");
561        assert_eq!(resource.get("id").unwrap(), "adahg32");
562
563        let re = ResourceDef::new("/{id:[[:digit:]]{6}}");
564        let tree = Tree::new(&re, 1);
565        assert_eq!(tree.find(&mut Path::new("/012345")), Some(1));
566        assert_eq!(tree.find(&mut Path::new("/012345/")), None);
567        assert_eq!(tree.find(&mut Path::new("/012345/index")), None);
568        assert_eq!(tree.find(&mut Path::new("/012")), None);
569        assert_eq!(tree.find(&mut Path::new("/01234567")), None);
570        assert_eq!(tree.find(&mut Path::new("/XXXXXX")), None);
571
572        let mut resource = Path::new("/012345");
573        assert_eq!(tree.find(&mut resource), Some(1));
574        assert_eq!(resource.get("id").unwrap(), "012345");
575
576        let re = ResourceDef::new("/u/test/v{version}-no-{minor}xx/resource/{id}/{name}");
577        let tree = Tree::new(&re, 1);
578        let mut resource = Path::new("/u/test/v1-no-3xx/resource/320120/name");
579        assert_eq!(tree.find(&mut resource), Some(1));
580        assert_eq!(resource.get("version").unwrap(), "1");
581        assert_eq!(resource.get("minor").unwrap(), "3");
582        assert_eq!(resource.get("id").unwrap(), "320120");
583        assert_eq!(resource.get("name").unwrap(), "name");
584    }
585
586    #[test]
587    fn test_dynamic_set() {
588        let re = ResourceDef::new(vec![
589            "/user/{id}",
590            "/v{version}/resource/{id}",
591            "/{id:[[:digit:]]{6}}",
592        ]);
593        let tree = Tree::new(&re, 1);
594        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
595        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
596        assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
597        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
598
599        let mut resource = Path::new("/user/profile");
600        assert_eq!(tree.find(&mut resource), Some(1));
601        assert_eq!(resource.get("id").unwrap(), "profile");
602
603        let mut resource = Path::new("/user/1245125");
604        assert_eq!(tree.find(&mut resource), Some(1));
605        assert_eq!(resource.get("id").unwrap(), "1245125");
606
607        assert_eq!(tree.find(&mut Path::new("/v1/resource/320120")), Some(1));
608        assert_eq!(tree.find(&mut Path::new("/v/resource/1")), None);
609        assert_eq!(tree.find(&mut Path::new("/resource")), None);
610
611        let mut resource = Path::new("/v151/resource/adahg32");
612        assert_eq!(tree.find(&mut resource), Some(1));
613        assert_eq!(resource.get("version").unwrap(), "151");
614        assert_eq!(resource.get("id").unwrap(), "adahg32");
615
616        assert_eq!(tree.find(&mut Path::new("/012345")), Some(1));
617        assert_eq!(tree.find(&mut Path::new("/012")), None);
618        assert_eq!(tree.find(&mut Path::new("/01234567")), None);
619        assert_eq!(tree.find(&mut Path::new("/XXXXXX")), None);
620
621        let mut resource = Path::new("/012345");
622        assert_eq!(tree.find(&mut resource), Some(1));
623        assert_eq!(resource.get("id").unwrap(), "012345");
624
625        let re = ResourceDef::new([
626            "/user/{id}",
627            "/v{version}/resource/{id}",
628            "/{id:[[:digit:]]{6}}",
629        ]);
630        let tree = Tree::new(&re, 1);
631        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
632        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
633        assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
634        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
635
636        let re = ResourceDef::new([
637            "/user/{id}".to_string(),
638            "/v{version}/resource/{id}".to_string(),
639            "/{id:[[:digit:]]{6}}".to_string(),
640        ]);
641        let tree = Tree::new(&re, 1);
642        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
643        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
644        assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
645        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
646    }
647
648    #[test]
649    fn test_parse_urlencoded() {
650        use urly::Url;
651
652        let tree = Tree::new(&ResourceDef::new("/user/{id}/test"), 1);
653        let uri = Url::try_from("/user/2345/test").unwrap();
654        let mut resource = Path::new(uri);
655        assert_eq!(tree.find(&mut resource), Some(1));
656        assert_eq!(resource.get("id").unwrap(), "2345");
657
658        let uri = Url::try_from("/user/qwe%25/test").unwrap();
659        let mut resource = Path::new(uri);
660        assert_eq!(tree.find(&mut resource), Some(1));
661        assert_eq!(resource.get("id").unwrap(), "qwe%");
662
663        let uri = Url::try_from("/user/qwe%25rty/test").unwrap();
664        let mut resource = Path::new(uri);
665        assert_eq!(tree.find(&mut resource), Some(1));
666        assert_eq!(resource.get("id").unwrap(), "qwe%rty");
667
668        let uri = Url::try_from("/user/foo-%2f-%252f-bar/test").unwrap();
669        let mut resource = Path::new(uri);
670        assert_eq!(tree.find(&mut resource), Some(1));
671        assert_eq!(resource.get("id").unwrap(), "foo-/-%2f-bar");
672
673        let uri =
674            Url::try_from("/user/http%3A%2F%2Flocalhost%3A80%2Ffile%2F%2Fvar%2Flog%2Fsyslog/test")
675                .unwrap();
676        let mut resource = Path::new(uri);
677        assert_eq!(tree.find(&mut resource), Some(1));
678        assert_eq!(
679            resource.get("id").unwrap(),
680            "http://localhost:80/file//var/log/syslog"
681        );
682    }
683
684    #[test]
685    fn test_extract_path_decode() {
686        use urly::Url;
687
688        let tree = Tree::new(&ResourceDef::new("/{id}/"), 1);
689
690        macro_rules! test_single_value {
691            ($value:expr, $expected:expr) => {{
692                let uri = Url::try_from($value).unwrap();
693                let mut resource = Path::new(uri);
694                assert_eq!(tree.find(&mut resource), Some(1));
695                assert_eq!(resource.get("id").unwrap(), $expected);
696            }};
697        }
698
699        test_single_value!("/%25/", "%");
700        test_single_value!("/%40%C2%A3%24%25%5E%26%2B%3D/", "@£$%^&+=");
701        test_single_value!("/%2B/", "+");
702        test_single_value!("/%252B/", "%2B");
703        test_single_value!("/%2F/", "/");
704        test_single_value!("/test%2Ftest/", "test/test");
705        test_single_value!("/%252F/", "%2F");
706        test_single_value!("/%m/", "%m");
707        test_single_value!("/%mm/", "%mm");
708        // decoded bytes that are not valid utf-8 are kept percent-encoded
709        test_single_value!("/%C3%A9/", "\u{e9}");
710        test_single_value!("/%FF%FE/", "%FF%FE");
711        test_single_value!("/test%C3/", "test%C3");
712        test_single_value!("/%C3%A9%FF/", "\u{e9}%FF");
713        test_single_value!("/%41%4/", "A%4");
714        test_single_value!("/%61b%62%/", "abb%");
715        test_single_value!("/a+b/", "a+b");
716        test_single_value!("/test%mm/", "test%mm");
717        test_single_value!(
718            "/http%3A%2F%2Flocalhost%3A80%2Ffoo/",
719            "http://localhost:80/foo"
720        );
721        test_single_value!("/%2Fvar%2Flog%2Fsyslog/", "/var/log/syslog");
722        test_single_value!(
723            "/http%3A%2F%2Flocalhost%3A80%2Ffile%2F%252Fvar%252Flog%252Fsyslog/",
724            "http://localhost:80/file/%2Fvar%2Flog%2Fsyslog"
725        );
726    }
727
728    #[test]
729    #[allow(clippy::mutable_key_type)]
730    fn test_def() {
731        let re = ResourceDef::new("/user/-{id}*");
732        assert_eq!(re, ResourceDef::from("/user/-{id}*"));
733        assert_eq!(re, ResourceDef::from("/user/-{id}*".to_string()));
734
735        let mut h = HashMap::new();
736        h.insert(re.clone(), 1);
737        assert!(h.contains_key(&re));
738
739        let seg = Segment::Static("s".to_string());
740        assert_eq!(seg, Segment::Static("s".to_string()));
741
742        let seg2 = Segment::Dynamic {
743            pattern: Regex::new("test").unwrap(),
744            names: Vec::new(),
745            tail: false,
746        };
747        assert_ne!(seg, seg2);
748        assert_eq!(seg2, seg2);
749    }
750
751    #[test]
752    #[allow(clippy::mutable_key_type)]
753    fn test_multiple_patterns() {
754        let re = ResourceDef::new(["/a/{x}", "/b/{x}"]);
755        assert_eq!(re.pattern(), "/a/{x}");
756        assert_eq!(re.patterns(), ["/a/{x}", "/b/{x}"]);
757
758        let tree = Tree::new(&re, 1);
759        assert_eq!(tree.find(&mut Path::new("/a/1")), Some(1));
760        assert_eq!(tree.find(&mut Path::new("/b/1")), Some(1));
761
762        // resource paths are built from the first pattern
763        let mut s = String::new();
764        assert!(re.build_path(&mut s, ["1"]));
765        assert_eq!(s, "/a/1");
766        let mut s = String::new();
767        let names: HashMap<_, _> = [("x", "2")].into_iter().collect();
768        assert!(re.build_path_named(&mut s, &names));
769        assert_eq!(s, "/a/2");
770
771        // all patterns are compared
772        assert_ne!(re, ResourceDef::new(["/c/{x}", "/b/{x}"]));
773        assert_ne!(re, ResourceDef::new("/a/{x}"));
774        assert_eq!(re, ResourceDef::new(["/a/{x}", "/b/{x}"]));
775        let mut h = HashMap::new();
776        h.insert(re.clone(), 1);
777        assert!(h.contains_key(&ResourceDef::new(["/a/{x}", "/b/{x}"])));
778        assert!(!h.contains_key(&ResourceDef::new("/b/{x}")));
779
780        let re = ResourceDef::new(Vec::<String>::new());
781        assert_eq!(re.pattern(), "");
782        assert_eq!(re.patterns(), Vec::<String>::new());
783    }
784
785    #[test]
786    fn test_leading_slash_is_optional() {
787        let tree = Tree::new(&ResourceDef::new("name/{id}"), 1);
788        let mut resource = Path::new("name/1");
789        assert_eq!(tree.find(&mut resource), Some(1));
790        assert_eq!(resource.get("id").unwrap(), "1");
791    }
792
793    #[test]
794    fn test_parse_escapes_literals() {
795        // literal after a dynamic segment
796        let mut resource = Path::new("/foo.json");
797        let tree = Tree::new(&ResourceDef::new("/{source}.json"), 1);
798        assert_eq!(tree.find(&mut resource), Some(1));
799        assert_eq!(resource.get("source").unwrap(), "foo");
800        assert_eq!(tree.find(&mut Path::new("/fooXjson")), None);
801
802        let tree = Tree::new(&ResourceDef::new("/{name}.[x]"), 1);
803        assert_eq!(tree.find(&mut Path::new("/a.[x]")), Some(1));
804        assert_eq!(tree.find(&mut Path::new("/a.x")), None);
805
806        // static tail
807        let tree = Tree::new(&ResourceDef::new("/files.v*"), 1);
808        assert_eq!(tree.find(&mut Path::new("/files.v1")), Some(1));
809        assert_eq!(tree.find(&mut Path::new("/filesXv1")), None);
810
811        let tree = Tree::new(&ResourceDef::new("/files(*"), 1);
812        assert_eq!(tree.find(&mut Path::new("/files(1")), Some(1));
813    }
814
815    #[test]
816    fn test_parse_tail() {
817        let re = ResourceDef::new("/user/-{id}*");
818        let tree = Tree::new(&re, 1);
819
820        let mut resource = Path::new("/user/-profile");
821        assert_eq!(tree.find(&mut resource), Some(1));
822        assert_eq!(resource.get("id").unwrap(), "profile");
823
824        let mut resource = Path::new("/user/-2345");
825        assert_eq!(tree.find(&mut resource), Some(1));
826        assert_eq!(resource.get("id").unwrap(), "2345");
827
828        let mut resource = Path::new("/user/-2345/");
829        assert_eq!(tree.find(&mut resource), Some(1));
830        assert_eq!(resource.get("id").unwrap(), "2345/");
831
832        let mut resource = Path::new("/user/-2345/sdg");
833        assert_eq!(tree.find(&mut resource), Some(1));
834        assert_eq!(resource.get("id").unwrap(), "2345/sdg");
835    }
836
837    #[test]
838    #[allow(clippy::needless_borrows_for_generic_args)]
839    fn test_static_tail() {
840        let re = ResourceDef::new("/*".to_string());
841        let tree = Tree::new(&re, 1);
842        assert_eq!(
843            tree.find(&mut Path::new(ntex_bytes::ByteString::from_static("/"))),
844            None
845        );
846        assert_eq!(tree.find(&mut Path::new("/profile")), Some(1));
847        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
848        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
849        assert_eq!(tree.find(&mut Path::new("/2345/")), Some(1));
850        assert_eq!(tree.find(&mut Path::new("/user/2345/")), Some(1));
851        assert_eq!(tree.find(&mut Path::new("/2345/sdg")), Some(1));
852        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), Some(1));
853
854        #[allow(clippy::needless_borrow)]
855        let re = ResourceDef::new(&("/user*".to_string()));
856        let tree = Tree::new(&re, 1);
857        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
858        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
859        assert_eq!(tree.find(&mut Path::new("/user/2345/")), Some(1));
860        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), Some(1));
861
862        let re = ResourceDef::new("/v/user*");
863        let tree = Tree::new(&re, 1);
864        assert_eq!(tree.find(&mut Path::new("/v/user/profile")), Some(1));
865        assert_eq!(tree.find(&mut Path::new("/v/user/2345")), Some(1));
866        assert_eq!(tree.find(&mut Path::new("/v/user/2345/")), Some(1));
867        assert_eq!(tree.find(&mut Path::new("/v/user/2345/sdg")), Some(1));
868
869        let re = ResourceDef::new("/user/*");
870        let tree = Tree::new(&re, 1);
871        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
872        assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
873        assert_eq!(tree.find(&mut Path::new("/user/2345/")), Some(1));
874        assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), Some(1));
875        assert_eq!(tree.find(&mut Path::new("/user/")), None);
876        assert_eq!(tree.find(&mut Path::new("/user")), None);
877
878        let re = ResourceDef::new("/v/user/*");
879        let tree = Tree::new(&re, 1);
880        assert_eq!(tree.find(&mut Path::new("/v/user/profile")), Some(1));
881        assert_eq!(tree.find(&mut Path::new("/v/user/2345")), Some(1));
882        assert_eq!(tree.find(&mut Path::new("/v/user/2345/")), Some(1));
883        assert_eq!(tree.find(&mut Path::new("/v/user/2345/sdg")), Some(1));
884        assert_eq!(tree.find(&mut Path::new("/v/user/")), None);
885        assert_eq!(tree.find(&mut Path::new("/v/user")), None);
886    }
887
888    #[test]
889    fn test_resource_prefix() {
890        let tree = Tree::new(&ResourceDef::prefix("/"), 1);
891        assert_eq!(tree.find(&mut Path::new("/")), Some(1));
892        assert_eq!(tree.find(&mut Path::new("/a")), Some(1));
893        assert_eq!(tree.find(&mut Path::new("/a/test/test")), Some(1));
894
895        let tree = Tree::new(&ResourceDef::prefix("/name"), 1);
896        assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
897        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
898        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
899        assert_eq!(tree.find(&mut Path::new("/name1")), None);
900        assert_eq!(tree.find(&mut Path::new("/name~")), None);
901
902        let mut resource = Path::new("/name/subpath1/subpath2/index.html");
903        assert_eq!(tree.find(&mut resource), Some(1));
904        assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
905
906        let tree = Tree::new(&ResourceDef::prefix("/name/"), 1);
907        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
908        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
909        assert_eq!(tree.find(&mut Path::new("/name")), None);
910        assert_eq!(tree.find(&mut Path::new("/name1")), None);
911
912        let tree = Tree::new(&ResourceDef::prefix(vec!["/name/", "/name2/"]), 1);
913        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
914        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
915        assert_eq!(tree.find(&mut Path::new("/name2/")), Some(1));
916        assert_eq!(tree.find(&mut Path::new("/name2/test/test")), Some(1));
917        assert_eq!(tree.find(&mut Path::new("/name")), None);
918        assert_eq!(tree.find(&mut Path::new("/name1")), None);
919
920        let tree = Tree::new(&ResourceDef::root_prefix("name/"), 1);
921        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
922        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
923        assert_eq!(tree.find(&mut Path::new("/name")), None);
924        assert_eq!(tree.find(&mut Path::new("/name1")), None);
925
926        let tree = Tree::new(&ResourceDef::root_prefix(vec!["name/", "name2/"]), 1);
927        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
928        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
929        assert_eq!(tree.find(&mut Path::new("/name2/")), Some(1));
930        assert_eq!(tree.find(&mut Path::new("/name2/test/test")), Some(1));
931        assert_eq!(tree.find(&mut Path::new("/name")), None);
932        assert_eq!(tree.find(&mut Path::new("/name1")), None);
933
934        let mut resource = Path::new("/name/subpath1/subpath2/index.html");
935        assert_eq!(tree.find(&mut resource), Some(1));
936        assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
937    }
938
939    #[test]
940    fn test_reousrce_prefix_dynamic() {
941        let tree = Tree::new(&ResourceDef::prefix("/{name}/"), 1);
942        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
943        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
944        assert_eq!(tree.find(&mut Path::new("/name")), None);
945        assert_eq!(tree.find(&mut Path::new("/name1")), None);
946        assert_eq!(tree.find(&mut Path::new("/name~")), None);
947
948        let mut resource = Path::new("/test2/");
949        assert_eq!(tree.find(&mut resource), Some(1));
950        assert_eq!(&resource["name"], "test2");
951        assert_eq!(&resource[0], "test2");
952
953        let mut resource = Path::new("/test2/subpath1/subpath2/index.html");
954        assert_eq!(tree.find(&mut resource), Some(1));
955        assert_eq!(&resource["name"], "test2");
956        assert_eq!(&resource[0], "test2");
957        assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
958
959        let tree = Tree::new(&ResourceDef::prefix("/{name}"), 1);
960        assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
961        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
962        assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
963        assert_eq!(tree.find(&mut Path::new("/name1")), Some(1));
964        assert_eq!(tree.find(&mut Path::new("/name~")), Some(1));
965
966        let tree = Tree::new(&ResourceDef::prefix(vec!["/1/{name}/", "/2/{name}/"]), 1);
967        assert_eq!(tree.find(&mut Path::new("/1/name/")), Some(1));
968        assert_eq!(tree.find(&mut Path::new("/1/name/test/test")), Some(1));
969        assert_eq!(tree.find(&mut Path::new("/2/name/")), Some(1));
970        assert_eq!(tree.find(&mut Path::new("/2/name/test/test")), Some(1));
971        assert_eq!(tree.find(&mut Path::new("/1/name")), None);
972        assert_eq!(tree.find(&mut Path::new("/1/name1")), None);
973        assert_eq!(tree.find(&mut Path::new("/1/name~")), None);
974        assert_eq!(tree.find(&mut Path::new("/2/name")), None);
975        assert_eq!(tree.find(&mut Path::new("/2/name1")), None);
976        assert_eq!(tree.find(&mut Path::new("/2/name~")), None);
977
978        let mut resource = Path::new("/1/test2/subpath1/subpath2/index.html");
979        assert_eq!(tree.find(&mut resource), Some(1));
980        assert_eq!(&resource["name"], "test2");
981        assert_eq!(&resource[0], "test2");
982        assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
983
984        let mut resource = Path::new("/2/test3/subpath1/subpath2/index.html");
985        assert_eq!(tree.find(&mut resource), Some(1));
986        assert_eq!(&resource["name"], "test3");
987        assert_eq!(&resource[0], "test3");
988        assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
989
990        // nested
991        let mut tree = Tree::new(&ResourceDef::prefix("/prefix/{v1}/second/{v2}"), 1);
992        tree.insert(&ResourceDef::prefix("/prefix/{v1}"), 2);
993
994        let mut resource = Path::new("/prefix/1/second/2");
995        assert_eq!(tree.find(&mut resource), Some(1));
996        assert_eq!(&resource["v1"], "1");
997        assert_eq!(&resource["v2"], "2");
998
999        let mut resource = Path::new("/prefix/1/second");
1000        assert_eq!(tree.find(&mut resource), Some(2));
1001        assert_eq!(&resource["v1"], "1");
1002        assert_eq!(tree.find(&mut Path::new("/prefix/1")), Some(2));
1003
1004        // nested
1005        let mut tree = Tree::new(
1006            &ResourceDef::prefix(vec![
1007                "/prefix/{v1}/second/{v2}",
1008                "/prefix2/{v1}/second/{v2}",
1009            ]),
1010            1,
1011        );
1012        tree.insert(&ResourceDef::prefix("/prefix/{v1}"), 2);
1013        tree.insert(&ResourceDef::prefix("/prefix2/{v1}"), 3);
1014
1015        let mut resource = Path::new("/prefix/1/second/2");
1016        assert_eq!(tree.find(&mut resource), Some(1));
1017        assert_eq!(&resource["v1"], "1");
1018        assert_eq!(&resource["v2"], "2");
1019
1020        let mut resource = Path::new("/prefix2/1/second/2");
1021        assert_eq!(tree.find(&mut resource), Some(1));
1022        assert_eq!(&resource["v1"], "1");
1023        assert_eq!(&resource["v2"], "2");
1024
1025        let mut resource = Path::new("/prefix/1/second");
1026        assert_eq!(tree.find(&mut resource), Some(2));
1027        assert_eq!(&resource["v1"], "1");
1028        assert_eq!(tree.find(&mut Path::new("/prefix/1")), Some(2));
1029
1030        let mut resource = Path::new("/prefix2/1/second");
1031        assert_eq!(tree.find(&mut resource), Some(3));
1032        assert_eq!(&resource["v1"], "1");
1033        assert_eq!(tree.find(&mut Path::new("/prefix2/1")), Some(3));
1034    }
1035
1036    #[test]
1037    fn test_resource_path() {
1038        let mut s = String::new();
1039        let resource = ResourceDef::new("/user/{item1}/test");
1040        assert!(resource.build_path(&mut s, ["user1"]));
1041        assert_eq!(s, "/user/user1/test");
1042
1043        let mut s = String::new();
1044        let resource = ResourceDef::new("/user/{item1}/{item2}/test");
1045        assert!(resource.build_path(&mut s, ["item", "item2"]));
1046        assert_eq!(s, "/user/item/item2/test");
1047
1048        let mut s = String::new();
1049        let resource = ResourceDef::new("/user/{item1}/{item2}");
1050        assert!(resource.build_path(&mut s, ["item", "item2"]));
1051        assert_eq!(s, "/user/item/item2");
1052
1053        let mut s = String::new();
1054        let resource = ResourceDef::new("/user/{item1}/{item2}/");
1055        assert!(resource.build_path(&mut s, ["item", "item2"]));
1056        assert_eq!(s, "/user/item/item2/");
1057
1058        let mut s = String::new();
1059        assert!(!resource.build_path(&mut s, ["item"]));
1060        assert_eq!(s, "");
1061
1062        let mut s = String::new();
1063        assert!(resource.build_path(&mut s, ["item", "item2"]));
1064        assert_eq!(s, "/user/item/item2/");
1065        assert!(!resource.build_path(&mut s, ["item"]));
1066        assert_eq!(s, "/user/item/item2/");
1067
1068        let mut s = String::new();
1069        assert!(resource.build_path(&mut s, vec!["item", "item2"]));
1070        assert_eq!(s, "/user/item/item2/");
1071
1072        let mut s = String::new();
1073        assert!(resource.build_path(&mut s, ["item".to_string(), "item2".to_string()]));
1074        assert_eq!(s, "/user/item/item2/");
1075
1076        // the iterator can be shared between several builders
1077        let mut s = String::new();
1078        let mut elements = ["a", "b", "c"].into_iter();
1079        assert!(ResourceDef::new("/{x}").build_path(&mut s, &mut elements));
1080        assert!(resource.build_path(&mut s, &mut elements));
1081        assert_eq!(s, "/a/user/b/c/");
1082        assert_eq!(elements.next(), None);
1083
1084        let mut map = HashMap::new();
1085        map.insert("item1", "item");
1086
1087        let mut s = "/prefix".to_string();
1088        assert!(!resource.build_path_named(&mut s, &map));
1089        assert_eq!(s, "/prefix");
1090
1091        let mut s = String::new();
1092        map.insert("item2", "item2");
1093        assert!(resource.build_path_named(&mut s, &map));
1094        assert_eq!(s, "/user/item/item2/");
1095    }
1096
1097    #[test]
1098    fn test_non_rooted() {
1099        let tree = Tree::new(&ResourceDef::new("name"), 1);
1100        assert_eq!(tree.find(&mut Path::new("name")), Some(1));
1101        assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
1102        assert_eq!(tree.find(&mut Path::new("/")), None);
1103        assert_eq!(tree.find(&mut Path::new("/name1")), None);
1104        assert_eq!(tree.find(&mut Path::new("/name/")), None);
1105        assert_eq!(tree.find(&mut Path::new("/name~")), None);
1106
1107        let tree = Tree::new(&ResourceDef::new("name/"), 1);
1108        assert_eq!(tree.find(&mut Path::new("name/")), Some(1));
1109        assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
1110        assert_eq!(tree.find(&mut Path::new("/name")), None);
1111        assert_eq!(tree.find(&mut Path::new("/name/gs")), None);
1112
1113        let tree = Tree::new(&ResourceDef::new("user/profile"), 1);
1114        assert_eq!(tree.find(&mut Path::new("user/profile")), Some(1));
1115        assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
1116        assert_eq!(tree.find(&mut Path::new("/user/profile/profile")), None);
1117
1118        let tree = Tree::new(&ResourceDef::new("{id}"), 1);
1119        assert_eq!(tree.find(&mut Path::new("profile")), Some(1));
1120        assert_eq!(tree.find(&mut Path::new("2345")), Some(1));
1121        assert_eq!(tree.find(&mut Path::new("/2345/")), None);
1122        assert_eq!(tree.find(&mut Path::new("/2345/sdg")), None);
1123
1124        let tree = Tree::new(&ResourceDef::new("{user}/profile/{no}"), 1);
1125        assert_eq!(tree.find(&mut Path::new("user/profile/123")), Some(1));
1126        assert_eq!(tree.find(&mut Path::new("/user/profile/123")), Some(1));
1127        assert_eq!(tree.find(&mut Path::new("/user/profile/p/test/")), None);
1128
1129        let tree = Tree::new(&ResourceDef::new("v{version}/resource/{id}/test"), 1);
1130        assert_eq!(
1131            tree.find(&mut Path::new("v1/resource/320120/test")),
1132            Some(1)
1133        );
1134        assert_eq!(tree.find(&mut Path::new("v/resource/1/test")), None);
1135
1136        let mut resource = Path::new("v151/resource/adahg32/test");
1137        assert_eq!(tree.find(&mut resource), Some(1));
1138        assert_eq!(resource.get("version").unwrap(), "151");
1139        assert_eq!(resource.get("id").unwrap(), "adahg32");
1140
1141        let re = ResourceDef::new("v/{id:[[:digit:]]{6}}");
1142        let tree = Tree::new(&re, 1);
1143        assert_eq!(tree.find(&mut Path::new("v/012345")), Some(1));
1144        assert_eq!(tree.find(&mut Path::new("v/012345/")), None);
1145        assert_eq!(tree.find(&mut Path::new("v/012345/index")), None);
1146        assert_eq!(tree.find(&mut Path::new("v/012")), None);
1147        assert_eq!(tree.find(&mut Path::new("v/01234567")), None);
1148        assert_eq!(tree.find(&mut Path::new("v/XXXXXX")), None);
1149
1150        let mut resource = Path::new("v/012345");
1151        assert_eq!(tree.find(&mut resource), Some(1));
1152        assert_eq!(resource.get("id").unwrap(), "012345");
1153
1154        let re = ResourceDef::new("u/test/v{version}-no-{minor}xx/resource/{id}/{name}");
1155        let tree = Tree::new(&re, 1);
1156        let mut resource = Path::new("u/test/v1-no-3xx/resource/320120/name");
1157        assert_eq!(tree.find(&mut resource), Some(1));
1158        assert_eq!(resource.get("version").unwrap(), "1");
1159        assert_eq!(resource.get("minor").unwrap(), "3");
1160        assert_eq!(resource.get("id").unwrap(), "320120");
1161        assert_eq!(resource.get("name").unwrap(), "name");
1162    }
1163
1164    #[test]
1165    fn test_recursive() {
1166        let mut tree = Tree::new(&ResourceDef::new("/name"), 1);
1167        tree.insert(&ResourceDef::new("/name/"), 2);
1168        tree.insert(&ResourceDef::new("/name/index.html"), 3);
1169        tree.insert(&ResourceDef::prefix("/"), 4);
1170
1171        assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
1172        assert_eq!(tree.find(&mut Path::new("/name/")), Some(2));
1173        assert_eq!(tree.find(&mut Path::new("/name/index.html")), Some(3));
1174        assert_eq!(tree.find(&mut Path::new("/")), Some(4));
1175        assert_eq!(tree.find(&mut Path::new("/test")), Some(4));
1176        assert_eq!(tree.find(&mut Path::new("/test/index.html")), Some(4));
1177    }
1178
1179    #[test]
1180    fn test_with_some_match() {
1181        let mut tree = Tree::new(&ResourceDef::new("/p/{tp}/{id}/{r}"), 1);
1182        tree.insert(&ResourceDef::new("/p/ih/{tp}/d/{id}/sid/{r}/r/{s}"), 3);
1183
1184        let mut p = Path::new("/p/ih/def/d/abc/sid/5bddc58f/r/srv");
1185        assert_eq!(tree.find(&mut p), Some(3));
1186        assert_eq!(p.get("tp"), Some("def"));
1187        assert_eq!(p.get("id"), Some("abc"));
1188        assert_eq!(p.get("r"), Some("5bddc58f"));
1189        assert_eq!(p.get("s"), Some("srv"));
1190        assert_eq!(p.len(), 4);
1191    }
1192
1193    #[test]
1194    fn test_array_patterns() {
1195        let pats: Vec<String> = (0..20).map(|i| format!("/p{i}")).collect();
1196        let arr: [&str; 20] = std::array::from_fn(|i| pats[i].as_str());
1197        let re = ResourceDef::new(arr);
1198        assert_eq!(re.patterns().len(), 20);
1199        assert_eq!(re.patterns()[19], "/p19");
1200
1201        let a = String::from("/a");
1202        let b = String::from("/b");
1203        let re = ResourceDef::new([&a, &b]);
1204        assert_eq!(re.patterns(), ["/a", "/b"]);
1205    }
1206}