diff --git a/src/application.rs b/src/application.rs index 822a7c472..e007ea71d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -10,6 +10,19 @@ use channel::HttpHandler; use pipeline::Pipeline; use middlewares::Middleware; +pub struct Router(Rc>>); + +impl Router { + pub fn new(prefix: String, map: HashMap>) -> Router + { + let mut resources = Vec::new(); + for (path, resource) in map { + resources.push((path, resource.get_name(), resource)) + } + + Router(Rc::new(RouteRecognizer::new(prefix, resources))) + } +} /// Application pub struct Application { @@ -17,7 +30,7 @@ pub struct Application { prefix: String, default: Resource, routes: Vec<(String, Route)>, - router: RouteRecognizer>, + router: Router, middlewares: Rc>>, } @@ -26,7 +39,7 @@ impl Application { fn run(&self, req: HttpRequest) -> Reply { let mut req = req.with_state(Rc::clone(&self.state)); - if let Some((params, h)) = self.router.recognize(req.path()) { + if let Some((params, h)) = self.router.0.recognize(req.path()) { if let Some(params) = params { req.set_match_info(params); } @@ -220,11 +233,6 @@ impl ApplicationBuilder where S: 'static { parts.prefix + "/" }; - let mut resources = Vec::new(); - for (path, handler) in parts.resources { - resources.push((path, handler)) - } - let mut routes = Vec::new(); for (path, route) in parts.routes { routes.push((prefix.clone() + path.trim_left_matches('/'), route)); @@ -234,7 +242,7 @@ impl ApplicationBuilder where S: 'static { prefix: prefix.clone(), default: parts.default, routes: routes, - router: RouteRecognizer::new(prefix, resources), + router: Router::new(prefix, parts.resources), middlewares: Rc::new(parts.middlewares), } } diff --git a/src/recognizer.rs b/src/recognizer.rs index 333904906..4b08aaece 100644 --- a/src/recognizer.rs +++ b/src/recognizer.rs @@ -201,7 +201,6 @@ FROM_STR!(std::net::SocketAddr); FROM_STR!(std::net::SocketAddrV4); FROM_STR!(std::net::SocketAddrV6); - pub struct RouteRecognizer { prefix: usize, patterns: RegexSet, @@ -222,13 +221,13 @@ impl Default for RouteRecognizer { impl RouteRecognizer { pub fn new, U>(prefix: P, routes: U) -> Self - where U: IntoIterator + where U: IntoIterator, T)> { let mut paths = Vec::new(); let mut handlers = Vec::new(); for item in routes { - let pat = parse(&item.0); - handlers.push((Pattern::new(&pat), item.1)); + let (pat, elements) = parse(&item.0); + handlers.push((Pattern::new(&pat, elements), item.2)); paths.push(pat); }; let regset = RegexSet::new(&paths); @@ -240,12 +239,12 @@ impl RouteRecognizer { } } - pub fn set_routes(&mut self, routes: Vec<(&str, T)>) { + pub fn set_routes(&mut self, routes: Vec<(&str, Option<&str>, T)>) { let mut paths = Vec::new(); let mut handlers = Vec::new(); for item in routes { - let pat = parse(item.0); - handlers.push((Pattern::new(&pat), item.1)); + let (pat, elements) = parse(item.0); + handlers.push((Pattern::new(&pat, elements), item.2)); paths.push(pat); }; self.patterns = RegexSet::new(&paths).unwrap(); @@ -276,13 +275,19 @@ impl RouteRecognizer { } } +enum PatternElement { + Str(String), + Var(String), +} + struct Pattern { re: Regex, names: Rc>, + elements: Vec, } impl Pattern { - fn new(pattern: &str) -> Self { + fn new(pattern: &str, elements: Vec) -> Self { let re = Regex::new(pattern).unwrap(); let names = re.capture_names() .enumerate() @@ -292,6 +297,7 @@ impl Pattern { Pattern { re, names: Rc::new(names), + elements: elements, } } @@ -306,19 +312,21 @@ impl Pattern { } pub(crate) fn check_pattern(path: &str) { - if let Err(err) = Regex::new(&parse(path)) { + if let Err(err) = Regex::new(&parse(path).0) { panic!("Wrong path pattern: \"{}\" {}", path, err); } } -fn parse(pattern: &str) -> String { +fn parse(pattern: &str) -> (String, Vec) { const DEFAULT_PATTERN: &str = "[^/]+"; let mut re = String::from("^/"); + let mut el = String::new(); 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); + let mut elems = Vec::new(); for (index, ch) in pattern.chars().enumerate() { // All routes must have a leading slash so its optional to have one @@ -329,6 +337,7 @@ fn parse(pattern: &str) -> String { if in_param { // In parameter segment: `{....}` if ch == '}' { + elems.push(PatternElement::Var(String::from(String::from(param_name.as_str())))); re.push_str(&format!(r"(?P<{}>{})", ¶m_name, ¶m_pattern)); param_name.clear(); @@ -350,13 +359,16 @@ fn parse(pattern: &str) -> String { } } else if ch == '{' { in_param = true; + elems.push(PatternElement::Str(String::from(el.as_str()))); + el.clear(); } else { re.push(ch); + el.push(ch); } } re.push('$'); - re + (re, elems) } #[cfg(test)] diff --git a/src/resource.rs b/src/resource.rs index 989d78f80..28fc1a0cf 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -59,6 +59,10 @@ impl Resource where S: 'static { self.name = name.into(); } + pub(crate) fn get_name(&self) -> Option { + if self.name.is_empty() { None } else { Some(self.name.clone()) } + } + /// Register a new route and return mutable reference to *Route* object. /// *Route* is used for route configuration, i.e. adding predicates, setting up handler. ///