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#[derive(Clone, Debug)]
21pub struct ResourceDef {
22 id: u16,
23 pub(super) tp: Vec<Segments>, 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 pub fn new<T: IntoPattern>(path: T) -> Self {
99 ResourceDef::create(path, false)
100 }
101
102 pub fn prefix<T: IntoPattern>(path: T) -> Self {
108 ResourceDef::create(path, true)
109 }
110
111 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 pub fn id(&self) -> u16 {
132 self.id
133 }
134
135 pub fn set_id(&mut self, id: u16) {
137 self.id = id;
138 }
139
140 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 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 pub fn name(&self) -> &str {
169 &self.name
170 }
171
172 pub fn set_name<N: Into<String>>(&mut self, name: N) {
174 self.name = name.into();
175 }
176
177 pub fn pattern(&self) -> &str {
179 self.patterns.first().map_or("", String::as_str)
180 }
181
182 pub fn patterns(&self) -> &[String] {
184 &self.patterns
185 }
186
187 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 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 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]; tail = rem == "*"; 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 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 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 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 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 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 if slesh {
402 elems.push(PathElement::Str("/".to_string()));
403 }
404
405 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 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 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 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 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 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 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 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 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}