diff --git a/src/recognizer.rs b/src/recognizer.rs deleted file mode 100644 index 79be1cb1..00000000 --- a/src/recognizer.rs +++ /dev/null @@ -1,192 +0,0 @@ -use regex::RegexSet; - -pub struct RouteRecognizer { - re: RegexSet, - routes: Vec, -} - -impl RouteRecognizer { - - pub fn new(routes: U) -> Self - where U: IntoIterator, K: Into, - { - let mut paths = Vec::new(); - let mut routes = Vec::new(); - for item in routes { - let pattern = parse(&item.0.into()); - paths.push(pattern); - routes.push(item.1); - }; - let regset = RegexSet::new(&paths); - - RouteRecognizer { - re: regset.unwrap(), - routes: routes, - } - } - - pub fn recognize(&self, path: &str) -> Option<&T> { - if path.is_empty() { - if let Some(idx) = self.re.matches("/").into_iter().next() { - return Some(&self.routes[idx]) - } - } else if let Some(idx) = self.re.matches(path).into_iter().next() { - return Some(&self.routes[idx]) - } - None - } -} - -fn parse(pattern: &str) -> String { - const DEFAULT_PATTERN: &str = "[^/]+"; - - let mut re = String::from("^/"); - let mut in_param = false; - let mut in_param_pattern = false; - let mut param_name = String::new(); - let mut param_pattern = String::from(DEFAULT_PATTERN); - - for (index, ch) in pattern.chars().enumerate() { - // All routes must have a leading slash so its optional to have one - if index == 0 && ch == '/' { - continue; - } - - if in_param { - // In parameter segment: `{....}` - if ch == '}' { - re.push_str(&format!(r"(?P<{}>{})", ¶m_name, ¶m_pattern)); - - param_name.clear(); - param_pattern = String::from(DEFAULT_PATTERN); - - in_param_pattern = false; - in_param = false; - } else if ch == ':' { - // The parameter name has been determined; custom pattern land - in_param_pattern = true; - param_pattern.clear(); - } else if in_param_pattern { - // Ignore leading whitespace for pattern - if !(ch == ' ' && param_pattern.is_empty()) { - param_pattern.push(ch); - } - } else { - param_name.push(ch); - } - } else if ch == '{' { - in_param = true; - } else { - re.push(ch); - } - } - - re.push('$'); - re -} - -#[cfg(test)] -mod tests { - use regex::Regex; - use super::*; - use std::iter::FromIterator; - - #[test] - fn test_recognizer() { - let routes = vec![ - ("/name", None, 1), - ("/name/{val}", None, 2), - ("/name/{val}/index.html", None, 3), - ("/v{val}/{val2}/index.html", None, 4), - ("/v/{tail:.*}", None, 5), - ]; - let rec = RouteRecognizer::new("", routes); - - let (params, val) = rec.recognize("/name").unwrap(); - assert_eq!(*val, 1); - assert!(params.unwrap().is_empty()); - - let (params, val) = rec.recognize("/name/value").unwrap(); - assert_eq!(*val, 2); - assert!(!params.as_ref().unwrap().is_empty()); - assert_eq!(params.as_ref().unwrap().get("val").unwrap(), "value"); - assert_eq!(¶ms.as_ref().unwrap()["val"], "value"); - - let (params, val) = rec.recognize("/name/value2/index.html").unwrap(); - assert_eq!(*val, 3); - assert!(!params.as_ref().unwrap().is_empty()); - assert_eq!(params.as_ref().unwrap().get("val").unwrap(), "value2"); - assert_eq!(params.as_ref().unwrap().by_idx(0).unwrap(), "value2"); - - let (params, val) = rec.recognize("/vtest/ttt/index.html").unwrap(); - assert_eq!(*val, 4); - assert!(!params.as_ref().unwrap().is_empty()); - assert_eq!(params.as_ref().unwrap().get("val").unwrap(), "test"); - assert_eq!(params.as_ref().unwrap().get("val2").unwrap(), "ttt"); - assert_eq!(params.as_ref().unwrap().by_idx(0).unwrap(), "test"); - assert_eq!(params.as_ref().unwrap().by_idx(1).unwrap(), "ttt"); - - let (params, val) = rec.recognize("/v/blah-blah/index.html").unwrap(); - assert_eq!(*val, 5); - assert!(!params.as_ref().unwrap().is_empty()); - assert_eq!(params.as_ref().unwrap().get("tail").unwrap(), "blah-blah/index.html"); - } - - 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"); - } -}