1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +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 pipeline::Pipeline;
use middlewares::Middleware; 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 /// Application
pub struct Application<S> { pub struct Application<S> {
@ -17,7 +30,7 @@ pub struct Application<S> {
prefix: String, prefix: String,
default: Resource<S>, default: Resource<S>,
routes: Vec<(String, Route<S>)>, routes: Vec<(String, Route<S>)>,
router: RouteRecognizer<Resource<S>>, router: Router<S>,
middlewares: Rc<Vec<Box<Middleware>>>, middlewares: Rc<Vec<Box<Middleware>>>,
} }
@ -26,7 +39,7 @@ impl<S: 'static> Application<S> {
fn run(&self, req: HttpRequest) -> Reply { fn run(&self, req: HttpRequest) -> Reply {
let mut req = req.with_state(Rc::clone(&self.state)); 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 { if let Some(params) = params {
req.set_match_info(params); req.set_match_info(params);
} }
@ -220,11 +233,6 @@ impl<S> ApplicationBuilder<S> where S: 'static {
parts.prefix + "/" parts.prefix + "/"
}; };
let mut resources = Vec::new();
for (path, handler) in parts.resources {
resources.push((path, handler))
}
let mut routes = Vec::new(); let mut routes = Vec::new();
for (path, route) in parts.routes { for (path, route) in parts.routes {
routes.push((prefix.clone() + path.trim_left_matches('/'), route)); routes.push((prefix.clone() + path.trim_left_matches('/'), route));
@ -234,7 +242,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
prefix: prefix.clone(), prefix: prefix.clone(),
default: parts.default, default: parts.default,
routes: routes, routes: routes,
router: RouteRecognizer::new(prefix, resources), router: Router::new(prefix, parts.resources),
middlewares: Rc::new(parts.middlewares), 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::SocketAddrV4);
FROM_STR!(std::net::SocketAddrV6); FROM_STR!(std::net::SocketAddrV6);
pub struct RouteRecognizer<T> { pub struct RouteRecognizer<T> {
prefix: usize, prefix: usize,
patterns: RegexSet, patterns: RegexSet,
@ -222,13 +221,13 @@ impl<T> Default for RouteRecognizer<T> {
impl<T> RouteRecognizer<T> { impl<T> RouteRecognizer<T> {
pub fn new<P: Into<String>, U>(prefix: P, routes: U) -> Self 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 paths = Vec::new();
let mut handlers = Vec::new(); let mut handlers = Vec::new();
for item in routes { for item in routes {
let pat = parse(&item.0); let (pat, elements) = parse(&item.0);
handlers.push((Pattern::new(&pat), item.1)); handlers.push((Pattern::new(&pat, elements), item.2));
paths.push(pat); paths.push(pat);
}; };
let regset = RegexSet::new(&paths); 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 paths = Vec::new();
let mut handlers = Vec::new(); let mut handlers = Vec::new();
for item in routes { for item in routes {
let pat = parse(item.0); let (pat, elements) = parse(item.0);
handlers.push((Pattern::new(&pat), item.1)); handlers.push((Pattern::new(&pat, elements), item.2));
paths.push(pat); paths.push(pat);
}; };
self.patterns = RegexSet::new(&paths).unwrap(); self.patterns = RegexSet::new(&paths).unwrap();
@ -276,13 +275,19 @@ impl<T> RouteRecognizer<T> {
} }
} }
enum PatternElement {
Str(String),
Var(String),
}
struct Pattern { struct Pattern {
re: Regex, re: Regex,
names: Rc<HashMap<String, usize>>, names: Rc<HashMap<String, usize>>,
elements: Vec<PatternElement>,
} }
impl Pattern { impl Pattern {
fn new(pattern: &str) -> Self { fn new(pattern: &str, elements: Vec<PatternElement>) -> Self {
let re = Regex::new(pattern).unwrap(); let re = Regex::new(pattern).unwrap();
let names = re.capture_names() let names = re.capture_names()
.enumerate() .enumerate()
@ -292,6 +297,7 @@ impl Pattern {
Pattern { Pattern {
re, re,
names: Rc::new(names), names: Rc::new(names),
elements: elements,
} }
} }
@ -306,19 +312,21 @@ impl Pattern {
} }
pub(crate) fn check_pattern(path: &str) { 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); panic!("Wrong path pattern: \"{}\" {}", path, err);
} }
} }
fn parse(pattern: &str) -> String { fn parse(pattern: &str) -> (String, Vec<PatternElement>) {
const DEFAULT_PATTERN: &str = "[^/]+"; const DEFAULT_PATTERN: &str = "[^/]+";
let mut re = String::from("^/"); let mut re = String::from("^/");
let mut el = String::new();
let mut in_param = false; let mut in_param = false;
let mut in_param_pattern = false; let mut in_param_pattern = false;
let mut param_name = String::new(); let mut param_name = String::new();
let mut param_pattern = String::from(DEFAULT_PATTERN); let mut param_pattern = String::from(DEFAULT_PATTERN);
let mut elems = Vec::new();
for (index, ch) in pattern.chars().enumerate() { for (index, ch) in pattern.chars().enumerate() {
// All routes must have a leading slash so its optional to have one // 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 { if in_param {
// In parameter segment: `{....}` // In parameter segment: `{....}`
if ch == '}' { if ch == '}' {
elems.push(PatternElement::Var(String::from(String::from(param_name.as_str()))));
re.push_str(&format!(r"(?P<{}>{})", &param_name, &param_pattern)); re.push_str(&format!(r"(?P<{}>{})", &param_name, &param_pattern));
param_name.clear(); param_name.clear();
@ -350,13 +359,16 @@ fn parse(pattern: &str) -> String {
} }
} else if ch == '{' { } else if ch == '{' {
in_param = true; in_param = true;
elems.push(PatternElement::Str(String::from(el.as_str())));
el.clear();
} else { } else {
re.push(ch); re.push(ch);
el.push(ch);
} }
} }
re.push('$'); re.push('$');
re (re, elems)
} }
#[cfg(test)] #[cfg(test)]

View File

@ -59,6 +59,10 @@ impl<S> Resource<S> where S: 'static {
self.name = name.into(); 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. /// Register a new route and return mutable reference to *Route* object.
/// *Route* is used for route configuration, i.e. adding predicates, setting up handler. /// *Route* is used for route configuration, i.e. adding predicates, setting up handler.
/// ///