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:
parent
3c9b6ea619
commit
a83d9b24ae
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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<{}>{})", ¶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)]
|
||||
|
@ -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.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user