1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-23 23:51:06 +01:00

extrat elements of path pattern

This commit is contained in:
Nikolay Kim 2017-12-05 11:31:35 -08:00
parent 3c9b6ea619
commit a83d9b24ae
3 changed files with 43 additions and 19 deletions

View File

@ -10,6 +10,19 @@ use channel::HttpHandler;
use pipeline::Pipeline;
use middlewares::Middleware;
pub struct Router<S>(Rc<RouteRecognizer<Resource<S>>>);
impl<S: 'static> Router<S> {
pub fn new(prefix: String, map: HashMap<String, Resource<S>>) -> Router<S>
{
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<S> {
@ -17,7 +30,7 @@ pub struct Application<S> {
prefix: String,
default: Resource<S>,
routes: Vec<(String, Route<S>)>,
router: RouteRecognizer<Resource<S>>,
router: Router<S>,
middlewares: Rc<Vec<Box<Middleware>>>,
}
@ -26,7 +39,7 @@ impl<S: 'static> Application<S> {
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<S> ApplicationBuilder<S> 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<S> ApplicationBuilder<S> 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),
}
}

View File

@ -201,7 +201,6 @@ FROM_STR!(std::net::SocketAddr);
FROM_STR!(std::net::SocketAddrV4);
FROM_STR!(std::net::SocketAddrV6);
pub struct RouteRecognizer<T> {
prefix: usize,
patterns: RegexSet,
@ -222,13 +221,13 @@ impl<T> Default for RouteRecognizer<T> {
impl<T> RouteRecognizer<T> {
pub fn new<P: Into<String>, U>(prefix: P, routes: U) -> Self
where U: IntoIterator<Item=(String, T)>
where U: IntoIterator<Item=(String, Option<String>, 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<T> RouteRecognizer<T> {
}
}
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<T> RouteRecognizer<T> {
}
}
enum PatternElement {
Str(String),
Var(String),
}
struct Pattern {
re: Regex,
names: Rc<HashMap<String, usize>>,
elements: Vec<PatternElement>,
}
impl Pattern {
fn new(pattern: &str) -> Self {
fn new(pattern: &str, elements: Vec<PatternElement>) -> 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<PatternElement>) {
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<{}>{})", &param_name, &param_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)]

View File

@ -59,6 +59,10 @@ impl<S> Resource<S> where S: 'static {
self.name = name.into();
}
pub(crate) fn get_name(&self) -> Option<String> {
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.
///