1use std::cmp::min;
2use std::collections::HashMap;
3use std::hash::{Hash, Hasher};
4
5use regex::{escape, Regex, RegexSet};
6
7use crate::path::{Path, PathItem};
8use crate::{IntoPattern, Resource, ResourcePath};
9
10const MAX_DYNAMIC_SEGMENTS: usize = 16;
11
12#[derive(Clone, Debug)]
16pub struct ResourceDef {
17 id: u16,
18 tp: PatternType,
19 name: String,
20 pattern: String,
21 elements: Vec<PatternElement>,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25enum PatternElement {
26 Str(String),
27 Var(String),
28}
29
30#[derive(Clone, Debug)]
31enum PatternType {
32 Static(String),
33 Prefix(String),
34 Dynamic(Regex, Vec<&'static str>, usize),
35 DynamicSet(RegexSet, Vec<(Regex, Vec<&'static str>, usize)>),
36}
37
38impl ResourceDef {
39 pub fn new<T: IntoPattern>(path: T) -> Self {
43 if path.is_single() {
44 let patterns = path.patterns();
45 ResourceDef::with_prefix(&patterns[0], false)
46 } else {
47 let set = path.patterns();
48 let mut data = Vec::new();
49 let mut re_set = Vec::new();
50
51 for path in set {
52 let (pattern, _, _, len) = ResourceDef::parse(&path, false);
53
54 let re = match Regex::new(&pattern) {
55 Ok(re) => re,
56 Err(err) => panic!("Wrong path pattern: \"{}\" {}", path, err),
57 };
58 let names: Vec<_> = re
60 .capture_names()
61 .filter_map(|name| {
62 name.map(|name| Box::leak(Box::new(name.to_owned())).as_str())
63 })
64 .collect();
65 data.push((re, names, len));
66 re_set.push(pattern);
67 }
68
69 ResourceDef {
70 id: 0,
71 tp: PatternType::DynamicSet(RegexSet::new(re_set).unwrap(), data),
72 elements: Vec::new(),
73 name: String::new(),
74 pattern: "".to_owned(),
75 }
76 }
77 }
78
79 pub fn prefix(path: &str) -> Self {
85 ResourceDef::with_prefix(path, true)
86 }
87
88 pub fn root_prefix(path: &str) -> Self {
96 ResourceDef::with_prefix(&insert_slash(path), true)
97 }
98
99 pub fn id(&self) -> u16 {
101 self.id
102 }
103
104 pub fn set_id(&mut self, id: u16) {
106 self.id = id;
107 }
108
109 fn with_prefix(path: &str, for_prefix: bool) -> Self {
111 let path = path.to_owned();
112 let (pattern, elements, is_dynamic, len) = ResourceDef::parse(&path, for_prefix);
113
114 let tp = if is_dynamic {
115 let re = match Regex::new(&pattern) {
116 Ok(re) => re,
117 Err(err) => panic!("Wrong path pattern: \"{}\" {}", path, err),
118 };
119 let names = re
121 .capture_names()
122 .filter_map(|name| {
123 name.map(|name| Box::leak(Box::new(name.to_owned())).as_str())
124 })
125 .collect();
126 PatternType::Dynamic(re, names, len)
127 } else if for_prefix {
128 PatternType::Prefix(pattern)
129 } else {
130 PatternType::Static(pattern)
131 };
132
133 ResourceDef {
134 tp,
135 elements,
136 id: 0,
137 name: String::new(),
138 pattern: path,
139 }
140 }
141
142 pub fn name(&self) -> &str {
144 &self.name
145 }
146
147 pub fn name_mut(&mut self) -> &mut String {
149 &mut self.name
150 }
151
152 pub fn pattern(&self) -> &str {
154 &self.pattern
155 }
156
157 #[inline]
158 pub fn is_match(&self, path: &str) -> bool {
160 match self.tp {
161 PatternType::Static(ref s) => s == path,
162 PatternType::Prefix(ref s) => path.starts_with(s),
163 PatternType::Dynamic(ref re, _, _) => re.is_match(path),
164 PatternType::DynamicSet(ref re, _) => re.is_match(path),
165 }
166 }
167
168 pub fn is_prefix_match(&self, path: &str) -> Option<usize> {
170 let plen = path.len();
171 let path = if path.is_empty() { "/" } else { path };
172
173 match self.tp {
174 PatternType::Static(ref s) => {
175 if s == path {
176 Some(plen)
177 } else {
178 None
179 }
180 }
181 PatternType::Dynamic(ref re, _, len) => {
182 if let Some(captures) = re.captures(path) {
183 let mut pos = 0;
184 let mut passed = false;
185 for capture in captures.iter() {
186 if let Some(ref m) = capture {
187 if !passed {
188 passed = true;
189 continue;
190 }
191
192 pos = m.end();
193 }
194 }
195 Some(pos + len)
196 } else {
197 None
198 }
199 }
200 PatternType::Prefix(ref s) => {
201 let len = if path == s {
202 s.len()
203 } else if path.starts_with(s)
204 && (s.ends_with('/') || path.split_at(s.len()).1.starts_with('/'))
205 {
206 if s.ends_with('/') {
207 s.len() - 1
208 } else {
209 s.len()
210 }
211 } else {
212 return None;
213 };
214 Some(min(plen, len))
215 }
216 PatternType::DynamicSet(ref re, ref params) => {
217 if let Some(idx) = re.matches(path).into_iter().next() {
218 let (ref pattern, _, len) = params[idx];
219 if let Some(captures) = pattern.captures(path) {
220 let mut pos = 0;
221 let mut passed = false;
222 for capture in captures.iter() {
223 if let Some(ref m) = capture {
224 if !passed {
225 passed = true;
226 continue;
227 }
228
229 pos = m.end();
230 }
231 }
232 Some(pos + len)
233 } else {
234 None
235 }
236 } else {
237 None
238 }
239 }
240 }
241 }
242
243 pub fn match_path<T: ResourcePath>(&self, path: &mut Path<T>) -> bool {
245 match self.tp {
246 PatternType::Static(ref s) => {
247 if s == path.path() {
248 path.skip(path.len() as u16);
249 true
250 } else {
251 false
252 }
253 }
254 PatternType::Prefix(ref s) => {
255 let rpath = path.path();
256 let len = if s == rpath {
257 s.len()
258 } else if rpath.starts_with(s)
259 && (s.ends_with('/') || rpath.split_at(s.len()).1.starts_with('/'))
260 {
261 if s.ends_with('/') {
262 s.len() - 1
263 } else {
264 s.len()
265 }
266 } else {
267 return false;
268 };
269 let rpath_len = rpath.len();
270 path.skip(min(rpath_len, len) as u16);
271 true
272 }
273 PatternType::Dynamic(ref re, ref names, len) => {
274 let mut idx = 0;
275 let mut pos = 0;
276 let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] =
277 [PathItem::Static(""); MAX_DYNAMIC_SEGMENTS];
278
279 if let Some(captures) = re.captures(path.path()) {
280 for (no, name) in names.iter().enumerate() {
281 if let Some(m) = captures.name(&name) {
282 idx += 1;
283 pos = m.end();
284 segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
285 } else {
286 log::error!(
287 "Dynamic path match but not all segments found: {}",
288 name
289 );
290 return false;
291 }
292 }
293 } else {
294 return false;
295 }
296 for idx in 0..idx {
297 path.add(names[idx].clone(), segments[idx]);
298 }
299 path.skip((pos + len) as u16);
300 true
301 }
302 PatternType::DynamicSet(ref re, ref params) => {
303 if let Some(idx) = re.matches(path.path()).into_iter().next() {
304 let (ref pattern, ref names, len) = params[idx];
305 let mut idx = 0;
306 let mut pos = 0;
307 let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] =
308 [PathItem::Static(""); MAX_DYNAMIC_SEGMENTS];
309
310 if let Some(captures) = pattern.captures(path.path()) {
311 for (no, name) in names.iter().enumerate() {
312 if let Some(m) = captures.name(&name) {
313 idx += 1;
314 pos = m.end();
315 segments[no] =
316 PathItem::Segment(m.start() as u16, m.end() as u16);
317 } else {
318 log::error!(
319 "Dynamic path match but not all segments found: {}",
320 name
321 );
322 return false;
323 }
324 }
325 } else {
326 return false;
327 }
328 for idx in 0..idx {
329 path.add(names[idx].clone(), segments[idx]);
330 }
331 path.skip((pos + len) as u16);
332 true
333 } else {
334 false
335 }
336 }
337 }
338 }
339
340 pub fn match_path_checked<R, T, F, U>(
342 &self,
343 res: &mut R,
344 check: &F,
345 user_data: &Option<U>,
346 ) -> bool
347 where
348 T: ResourcePath,
349 R: Resource<T>,
350 F: Fn(&R, &Option<U>) -> bool,
351 {
352 match self.tp {
353 PatternType::Static(ref s) => {
354 if s == res.resource_path().path() && check(res, user_data) {
355 let path = res.resource_path();
356 path.skip(path.len() as u16);
357 true
358 } else {
359 false
360 }
361 }
362 PatternType::Prefix(ref s) => {
363 let len = {
364 let rpath = res.resource_path().path();
365 if s == rpath {
366 s.len()
367 } else if rpath.starts_with(s)
368 && (s.ends_with('/') || rpath.split_at(s.len()).1.starts_with('/'))
369 {
370 if s.ends_with('/') {
371 s.len() - 1
372 } else {
373 s.len()
374 }
375 } else {
376 return false;
377 }
378 };
379 if !check(res, user_data) {
380 return false;
381 }
382 let path = res.resource_path();
383 path.skip(min(path.path().len(), len) as u16);
384 true
385 }
386 PatternType::Dynamic(ref re, ref names, len) => {
387 let mut idx = 0;
388 let mut pos = 0;
389 let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] =
390 [PathItem::Static(""); MAX_DYNAMIC_SEGMENTS];
391
392 if let Some(captures) = re.captures(res.resource_path().path()) {
393 for (no, name) in names.iter().enumerate() {
394 if let Some(m) = captures.name(&name) {
395 idx += 1;
396 pos = m.end();
397 segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
398 } else {
399 log::error!(
400 "Dynamic path match but not all segments found: {}",
401 name
402 );
403 return false;
404 }
405 }
406 } else {
407 return false;
408 }
409
410 if !check(res, user_data) {
411 return false;
412 }
413
414 let path = res.resource_path();
415 for idx in 0..idx {
416 path.add(names[idx].clone(), segments[idx]);
417 }
418 path.skip((pos + len) as u16);
419 true
420 }
421 PatternType::DynamicSet(ref re, ref params) => {
422 let path = res.resource_path().path();
423 if let Some(idx) = re.matches(path).into_iter().next() {
424 let (ref pattern, ref names, len) = params[idx];
425 let mut idx = 0;
426 let mut pos = 0;
427 let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] =
428 [PathItem::Static(""); MAX_DYNAMIC_SEGMENTS];
429
430 if let Some(captures) = pattern.captures(path) {
431 for (no, name) in names.iter().enumerate() {
432 if let Some(m) = captures.name(&name) {
433 idx += 1;
434 pos = m.end();
435 segments[no] =
436 PathItem::Segment(m.start() as u16, m.end() as u16);
437 } else {
438 log::error!(
439 "Dynamic path match but not all segments found: {}",
440 name
441 );
442 return false;
443 }
444 }
445 } else {
446 return false;
447 }
448
449 if !check(res, user_data) {
450 return false;
451 }
452
453 let path = res.resource_path();
454 for idx in 0..idx {
455 path.add(names[idx].clone(), segments[idx]);
456 }
457 path.skip((pos + len) as u16);
458 true
459 } else {
460 false
461 }
462 }
463 }
464 }
465
466 pub fn resource_path<U, I>(&self, path: &mut String, elements: &mut U) -> bool
468 where
469 U: Iterator<Item = I>,
470 I: AsRef<str>,
471 {
472 match self.tp {
473 PatternType::Prefix(ref p) => path.push_str(p),
474 PatternType::Static(ref p) => path.push_str(p),
475 PatternType::Dynamic(..) => {
476 for el in &self.elements {
477 match *el {
478 PatternElement::Str(ref s) => path.push_str(s),
479 PatternElement::Var(_) => {
480 if let Some(val) = elements.next() {
481 path.push_str(val.as_ref())
482 } else {
483 return false;
484 }
485 }
486 }
487 }
488 }
489 PatternType::DynamicSet(..) => {
490 return false;
491 }
492 }
493 true
494 }
495
496 pub fn resource_path_named<K, V, S>(
498 &self,
499 path: &mut String,
500 elements: &HashMap<K, V, S>,
501 ) -> bool
502 where
503 K: std::borrow::Borrow<str> + Eq + Hash,
504 V: AsRef<str>,
505 S: std::hash::BuildHasher,
506 {
507 match self.tp {
508 PatternType::Prefix(ref p) => path.push_str(p),
509 PatternType::Static(ref p) => path.push_str(p),
510 PatternType::Dynamic(..) => {
511 for el in &self.elements {
512 match *el {
513 PatternElement::Str(ref s) => path.push_str(s),
514 PatternElement::Var(ref name) => {
515 if let Some(val) = elements.get(name) {
516 path.push_str(val.as_ref())
517 } else {
518 return false;
519 }
520 }
521 }
522 }
523 }
524 PatternType::DynamicSet(..) => {
525 return false;
526 }
527 }
528 true
529 }
530
531 fn parse_param(pattern: &str) -> (PatternElement, String, &str, bool) {
532 const DEFAULT_PATTERN: &str = "[^/]+";
533 const DEFAULT_PATTERN_TAIL: &str = ".*";
534 let mut params_nesting = 0usize;
535 let close_idx = pattern
536 .find(|c| match c {
537 '{' => {
538 params_nesting += 1;
539 false
540 }
541 '}' => {
542 params_nesting -= 1;
543 params_nesting == 0
544 }
545 _ => false,
546 })
547 .expect("malformed dynamic segment");
548 let (mut param, mut rem) = pattern.split_at(close_idx + 1);
549 param = ¶m[1..param.len() - 1]; let tail = rem == "*";
551
552 let (name, pattern) = match param.find(':') {
553 Some(idx) => {
554 if tail {
555 panic!("Custom regex is not supported for remainder match");
556 }
557 let (name, pattern) = param.split_at(idx);
558 (name, &pattern[1..])
559 }
560 None => (
561 param,
562 if tail {
563 rem = &rem[1..];
564 DEFAULT_PATTERN_TAIL
565 } else {
566 DEFAULT_PATTERN
567 },
568 ),
569 };
570 (
571 PatternElement::Var(name.to_string()),
572 format!(r"(?P<{}>{})", &name, &pattern),
573 rem,
574 tail,
575 )
576 }
577
578 fn parse(
579 mut pattern: &str,
580 mut for_prefix: bool,
581 ) -> (String, Vec<PatternElement>, bool, usize) {
582 if pattern.find('{').is_none() {
583 return if pattern.ends_with('*') {
584 let path = &pattern[..pattern.len() - 1];
585 let re = String::from("^") + path + "(.*)";
586 (re, vec![PatternElement::Str(String::from(path))], true, 0)
587 } else {
588 (
589 String::from(pattern),
590 vec![PatternElement::Str(String::from(pattern))],
591 false,
592 pattern.chars().count(),
593 )
594 };
595 }
596
597 let mut elems = Vec::new();
598 let mut re = String::from("^");
599 let mut dyn_elems = 0;
600
601 while let Some(idx) = pattern.find('{') {
602 let (prefix, rem) = pattern.split_at(idx);
603 elems.push(PatternElement::Str(String::from(prefix)));
604 re.push_str(&escape(prefix));
605 let (param_pattern, re_part, rem, tail) = Self::parse_param(rem);
606 if tail {
607 for_prefix = true;
608 }
609
610 elems.push(param_pattern);
611 re.push_str(&re_part);
612 pattern = rem;
613 dyn_elems += 1;
614 }
615
616 elems.push(PatternElement::Str(String::from(pattern)));
617 re.push_str(&escape(pattern));
618
619 if dyn_elems > MAX_DYNAMIC_SEGMENTS {
620 panic!(
621 "Only {} dynanic segments are allowed, provided: {}",
622 MAX_DYNAMIC_SEGMENTS, dyn_elems
623 );
624 }
625
626 if !for_prefix {
627 re.push_str("$");
628 }
629 (re, elems, true, pattern.chars().count())
630 }
631}
632
633impl Eq for ResourceDef {}
634
635impl PartialEq for ResourceDef {
636 fn eq(&self, other: &ResourceDef) -> bool {
637 self.pattern == other.pattern
638 }
639}
640
641impl Hash for ResourceDef {
642 fn hash<H: Hasher>(&self, state: &mut H) {
643 self.pattern.hash(state);
644 }
645}
646
647impl<'a> From<&'a str> for ResourceDef {
648 fn from(path: &'a str) -> ResourceDef {
649 ResourceDef::new(path)
650 }
651}
652
653impl From<String> for ResourceDef {
654 fn from(path: String) -> ResourceDef {
655 ResourceDef::new(path)
656 }
657}
658
659pub(crate) fn insert_slash(path: &str) -> String {
660 let mut path = path.to_owned();
661 if !path.is_empty() && !path.starts_with('/') {
662 path.insert(0, '/');
663 };
664 path
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670 use http::Uri;
671 use std::convert::TryFrom;
672
673 #[test]
674 fn test_parse_static() {
675 let re = ResourceDef::new("/");
676 assert!(re.is_match("/"));
677 assert!(!re.is_match("/a"));
678
679 let re = ResourceDef::new("/name");
680 assert!(re.is_match("/name"));
681 assert!(!re.is_match("/name1"));
682 assert!(!re.is_match("/name/"));
683 assert!(!re.is_match("/name~"));
684
685 assert_eq!(re.is_prefix_match("/name"), Some(5));
686 assert_eq!(re.is_prefix_match("/name1"), None);
687 assert_eq!(re.is_prefix_match("/name/"), None);
688 assert_eq!(re.is_prefix_match("/name~"), None);
689
690 let re = ResourceDef::new("/name/");
691 assert!(re.is_match("/name/"));
692 assert!(!re.is_match("/name"));
693 assert!(!re.is_match("/name/gs"));
694
695 let re = ResourceDef::new("/user/profile");
696 assert!(re.is_match("/user/profile"));
697 assert!(!re.is_match("/user/profile/profile"));
698 }
699
700 #[test]
701 fn test_parse_param() {
702 let re = ResourceDef::new("/user/{id}");
703 assert!(re.is_match("/user/profile"));
704 assert!(re.is_match("/user/2345"));
705 assert!(!re.is_match("/user/2345/"));
706 assert!(!re.is_match("/user/2345/sdg"));
707
708 let mut path = Path::new("/user/profile");
709 assert!(re.match_path(&mut path));
710 assert_eq!(path.get("id").unwrap(), "profile");
711
712 let mut path = Path::new("/user/1245125");
713 assert!(re.match_path(&mut path));
714 assert_eq!(path.get("id").unwrap(), "1245125");
715
716 let re = ResourceDef::new("/v{version}/resource/{id}");
717 assert!(re.is_match("/v1/resource/320120"));
718 assert!(!re.is_match("/v/resource/1"));
719 assert!(!re.is_match("/resource"));
720
721 let mut path = Path::new("/v151/resource/adahg32");
722 assert!(re.match_path(&mut path));
723 assert_eq!(path.get("version").unwrap(), "151");
724 assert_eq!(path.get("id").unwrap(), "adahg32");
725
726 let re = ResourceDef::new("/{id:[[:digit:]]{6}}");
727 assert!(re.is_match("/012345"));
728 assert!(!re.is_match("/012"));
729 assert!(!re.is_match("/01234567"));
730 assert!(!re.is_match("/XXXXXX"));
731
732 let mut path = Path::new("/012345");
733 assert!(re.match_path(&mut path));
734 assert_eq!(path.get("id").unwrap(), "012345");
735 }
736
737 #[test]
738 fn test_dynamic_set() {
739 let re = ResourceDef::new(vec![
740 "/user/{id}",
741 "/v{version}/resource/{id}",
742 "/{id:[[:digit:]]{6}}",
743 ]);
744 assert!(re.is_match("/user/profile"));
745 assert!(re.is_match("/user/2345"));
746 assert!(!re.is_match("/user/2345/"));
747 assert!(!re.is_match("/user/2345/sdg"));
748
749 let mut path = Path::new("/user/profile");
750 assert!(re.match_path(&mut path));
751 assert_eq!(path.get("id").unwrap(), "profile");
752
753 let mut path = Path::new("/user/1245125");
754 assert!(re.match_path(&mut path));
755 assert_eq!(path.get("id").unwrap(), "1245125");
756
757 assert!(re.is_match("/v1/resource/320120"));
758 assert!(!re.is_match("/v/resource/1"));
759 assert!(!re.is_match("/resource"));
760
761 let mut path = Path::new("/v151/resource/adahg32");
762 assert!(re.match_path(&mut path));
763 assert_eq!(path.get("version").unwrap(), "151");
764 assert_eq!(path.get("id").unwrap(), "adahg32");
765
766 assert!(re.is_match("/012345"));
767 assert!(!re.is_match("/012"));
768 assert!(!re.is_match("/01234567"));
769 assert!(!re.is_match("/XXXXXX"));
770
771 let mut path = Path::new("/012345");
772 assert!(re.match_path(&mut path));
773 assert_eq!(path.get("id").unwrap(), "012345");
774
775 let re = ResourceDef::new([
776 "/user/{id}",
777 "/v{version}/resource/{id}",
778 "/{id:[[:digit:]]{6}}",
779 ]);
780 assert!(re.is_match("/user/profile"));
781 assert!(re.is_match("/user/2345"));
782 assert!(!re.is_match("/user/2345/"));
783 assert!(!re.is_match("/user/2345/sdg"));
784
785 let re = ResourceDef::new([
786 "/user/{id}".to_string(),
787 "/v{version}/resource/{id}".to_string(),
788 "/{id:[[:digit:]]{6}}".to_string(),
789 ]);
790 assert!(re.is_match("/user/profile"));
791 assert!(re.is_match("/user/2345"));
792 assert!(!re.is_match("/user/2345/"));
793 assert!(!re.is_match("/user/2345/sdg"));
794 }
795
796 #[test]
797 fn test_parse_tail() {
798 let re = ResourceDef::new("/user/-{id}*");
799
800 let mut path = Path::new("/user/-profile");
801 assert!(re.match_path(&mut path));
802 assert_eq!(path.get("id").unwrap(), "profile");
803
804 let mut path = Path::new("/user/-2345");
805 assert!(re.match_path(&mut path));
806 assert_eq!(path.get("id").unwrap(), "2345");
807
808 let mut path = Path::new("/user/-2345/");
809 assert!(re.match_path(&mut path));
810 assert_eq!(path.get("id").unwrap(), "2345/");
811
812 let mut path = Path::new("/user/-2345/sdg");
813 assert!(re.match_path(&mut path));
814 assert_eq!(path.get("id").unwrap(), "2345/sdg");
815 }
816
817 #[test]
818 fn test_static_tail() {
819 let re = ResourceDef::new("/user*");
820 assert!(re.is_match("/user/profile"));
821 assert!(re.is_match("/user/2345"));
822 assert!(re.is_match("/user/2345/"));
823 assert!(re.is_match("/user/2345/sdg"));
824
825 let re = ResourceDef::new("/user/*");
826 assert!(re.is_match("/user/profile"));
827 assert!(re.is_match("/user/2345"));
828 assert!(re.is_match("/user/2345/"));
829 assert!(re.is_match("/user/2345/sdg"));
830 }
831
832 #[test]
833 fn test_parse_urlencoded_param() {
834 let re = ResourceDef::new("/user/{id}/test");
835
836 let mut path = Path::new("/user/2345/test");
837 assert!(re.match_path(&mut path));
838 assert_eq!(path.get("id").unwrap(), "2345");
839
840 let mut path = Path::new("/user/qwe%25/test");
841 assert!(re.match_path(&mut path));
842 assert_eq!(path.get("id").unwrap(), "qwe%25");
843
844 let uri = Uri::try_from("/user/qwe%25/test").unwrap();
845 let mut path = Path::new(uri);
846 assert!(re.match_path(&mut path));
847 assert_eq!(path.get("id").unwrap(), "qwe%25");
848 }
849
850 #[test]
851 fn test_resource_prefix() {
852 let re = ResourceDef::prefix("/name");
853 assert!(re.is_match("/name"));
854 assert!(re.is_match("/name/"));
855 assert!(re.is_match("/name/test/test"));
856 assert!(re.is_match("/name1"));
857 assert!(re.is_match("/name~"));
858
859 assert_eq!(re.is_prefix_match("/name"), Some(5));
860 assert_eq!(re.is_prefix_match("/name/"), Some(5));
861 assert_eq!(re.is_prefix_match("/name/test/test"), Some(5));
862 assert_eq!(re.is_prefix_match("/name1"), None);
863 assert_eq!(re.is_prefix_match("/name~"), None);
864
865 let re = ResourceDef::prefix("/name/");
866 assert!(re.is_match("/name/"));
867 assert!(re.is_match("/name/gs"));
868 assert!(!re.is_match("/name"));
869
870 let re = ResourceDef::root_prefix("name/");
871 assert!(re.is_match("/name/"));
872 assert!(re.is_match("/name/gs"));
873 assert!(!re.is_match("/name"));
874 }
875
876 #[test]
877 fn test_reousrce_prefix_dynamic() {
878 let re = ResourceDef::prefix("/{name}/");
879 assert!(re.is_match("/name/"));
880 assert!(re.is_match("/name/gs"));
881 assert!(!re.is_match("/name"));
882
883 assert_eq!(re.is_prefix_match("/name/"), Some(6));
884 assert_eq!(re.is_prefix_match("/name/gs"), Some(6));
885 assert_eq!(re.is_prefix_match("/name"), None);
886
887 let mut path = Path::new("/test2/");
888 assert!(re.match_path(&mut path));
889 assert_eq!(&path["name"], "test2");
890 assert_eq!(&path[0], "test2");
891
892 let mut path = Path::new("/test2/subpath1/subpath2/index.html");
893 assert!(re.match_path(&mut path));
894 assert_eq!(&path["name"], "test2");
895 assert_eq!(&path[0], "test2");
896 }
897
898 #[test]
899 fn test_resource_path() {
900 let mut s = String::new();
901 let resource = ResourceDef::new("/user/{item1}/test");
902 assert!(resource.resource_path(&mut s, &mut (&["user1"]).into_iter()));
903 assert_eq!(s, "/user/user1/test");
904
905 let mut s = String::new();
906 let resource = ResourceDef::new("/user/{item1}/{item2}/test");
907 assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
908 assert_eq!(s, "/user/item/item2/test");
909
910 let mut s = String::new();
911 let resource = ResourceDef::new("/user/{item1}/{item2}");
912 assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
913 assert_eq!(s, "/user/item/item2");
914
915 let mut s = String::new();
916 let resource = ResourceDef::new("/user/{item1}/{item2}/");
917 assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
918 assert_eq!(s, "/user/item/item2/");
919
920 let mut s = String::new();
921 assert!(!resource.resource_path(&mut s, &mut (&["item"]).into_iter()));
922
923 let mut s = String::new();
924 assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
925 assert_eq!(s, "/user/item/item2/");
926 assert!(!resource.resource_path(&mut s, &mut (&["item"]).into_iter()));
927
928 let mut s = String::new();
929 assert!(resource.resource_path(&mut s, &mut vec!["item", "item2"].into_iter()));
930 assert_eq!(s, "/user/item/item2/");
931
932 let mut map = HashMap::new();
933 map.insert("item1", "item");
934
935 let mut s = String::new();
936 assert!(!resource.resource_path_named(&mut s, &map));
937
938 let mut s = String::new();
939 map.insert("item2", "item2");
940 assert!(resource.resource_path_named(&mut s, &map));
941 assert_eq!(s, "/user/item/item2/");
942 }
943}