From f53f35f364ce9dfd4e8bf9ae23e5f93af3eb95c9 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 30 Nov 2017 15:48:09 -0800 Subject: [PATCH] added tail pattern --- src/recognizer.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/src/recognizer.rs b/src/recognizer.rs index 480e5a89..1a1b9a52 100644 --- a/src/recognizer.rs +++ b/src/recognizer.rs @@ -117,6 +117,7 @@ pub(crate) fn check_pattern(path: &str) { fn parse(pattern: &str) -> String { const DEFAULT_PATTERN: &str = "[^/]+"; + let mut hard_stop = false; let mut re = String::from("^/"); let mut in_param = false; let mut in_param_pattern = false; @@ -129,10 +130,20 @@ fn parse(pattern: &str) -> String { continue; } + if hard_stop { + panic!("{id:*} section has to be last lection of pattern"); + } + if in_param { // In parameter segment: `{....}` if ch == '}' { - re.push_str(&format!(r"(?P<{}>{})", ¶m_name, ¶m_pattern)); + if param_pattern == "*" { + hard_stop = true; + re.push_str( + &format!(r"(?P<{}>[%/[:word:][:punct:][:space:]]+)", ¶m_name)); + } else { + re.push_str(&format!(r"(?P<{}>{})", ¶m_name, ¶m_pattern)); + } param_name.clear(); param_pattern = String::from(DEFAULT_PATTERN); @@ -211,3 +222,88 @@ impl Params { self.names.get(key).and_then(|&i| self.by_idx(i - 1)) } } + +#[cfg(test)] +mod tests { + use regex::Regex; + use super::*; + + fn assert_parse(pattern: &str, expected_re: &str) -> Regex { + let re_str = parse(pattern); + assert_eq!(&*re_str, expected_re); + Regex::new(&re_str).unwrap() + } + + #[test] + fn test_parse_static() { + let re = assert_parse("/", r"^/$"); + assert!(re.is_match("/")); + assert!(!re.is_match("/a")); + + let re = assert_parse("/name", r"^/name$"); + assert!(re.is_match("/name")); + assert!(!re.is_match("/name1")); + assert!(!re.is_match("/name/")); + assert!(!re.is_match("/name~")); + + let re = assert_parse("/name/", r"^/name/$"); + assert!(re.is_match("/name/")); + assert!(!re.is_match("/name")); + assert!(!re.is_match("/name/gs")); + + let re = assert_parse("/user/profile", r"^/user/profile$"); + assert!(re.is_match("/user/profile")); + assert!(!re.is_match("/user/profile/profile")); + } + + #[test] + fn test_parse_param() { + let re = assert_parse("/user/{id}", r"^/user/(?P[^/]+)$"); + assert!(re.is_match("/user/profile")); + assert!(re.is_match("/user/2345")); + assert!(!re.is_match("/user/2345/")); + assert!(!re.is_match("/user/2345/sdg")); + + let captures = re.captures("/user/profile").unwrap(); + assert_eq!(captures.get(1).unwrap().as_str(), "profile"); + assert_eq!(captures.name("id").unwrap().as_str(), "profile"); + + let captures = re.captures("/user/1245125").unwrap(); + assert_eq!(captures.get(1).unwrap().as_str(), "1245125"); + assert_eq!(captures.name("id").unwrap().as_str(), "1245125"); + + let re = assert_parse( + "/v{version}/resource/{id}", + r"^/v(?P[^/]+)/resource/(?P[^/]+)$", + ); + assert!(re.is_match("/v1/resource/320120")); + assert!(!re.is_match("/v/resource/1")); + assert!(!re.is_match("/resource")); + + let captures = re.captures("/v151/resource/adahg32").unwrap(); + assert_eq!(captures.get(1).unwrap().as_str(), "151"); + assert_eq!(captures.name("version").unwrap().as_str(), "151"); + assert_eq!(captures.name("id").unwrap().as_str(), "adahg32"); + } + + #[test] + fn test_tail_param() { + let re = assert_parse("/user/{tail:*}", + r"^/user/(?P[%/[:word:][:punct:][:space:]]+)$"); + assert!(re.is_match("/user/profile")); + assert!(re.is_match("/user/2345")); + assert!(re.is_match("/user/2345/")); + assert!(re.is_match("/user/2345/sdg")); + assert!(re.is_match("/user/2345/sd-_g/")); + assert!(re.is_match("/user/2345/sdg/asddsasd/index.html")); + + let re = assert_parse("/user/v{tail:*}", + r"^/user/v(?P[%/[:word:][:punct:][:space:]]+)$"); + assert!(!re.is_match("/user/2345/")); + assert!(re.is_match("/user/vprofile")); + assert!(re.is_match("/user/v_2345")); + assert!(re.is_match("/user/v2345/sdg")); + assert!(re.is_match("/user/v2345/sd-_g/test.html")); + assert!(re.is_match("/user/v/sdg/asddsasd/index.html")); + } +}