1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

work on resource_path api

This commit is contained in:
Nikolay Kim 2017-12-05 13:31:06 -08:00
parent 3de43c2a46
commit d8b880e167
6 changed files with 82 additions and 35 deletions

View File

@ -27,7 +27,7 @@ fn index(mut req: HttpRequest) -> Result<HttpResponse> {
req.session().set("counter", 1)?;
}
Ok(HttpResponse::Ok().into())
Ok("Welcome!".into())
}
/// async handler

View File

@ -1,10 +1,11 @@
use std::rc::Rc;
use std::collections::HashMap;
use error::UriGenerationError;
use handler::{Reply, RouteHandler};
use route::Route;
use resource::Resource;
use recognizer::{RouteRecognizer, check_pattern};
use recognizer::{RouteRecognizer, check_pattern, PatternElement};
use httprequest::HttpRequest;
use channel::HttpHandler;
use pipeline::Pipeline;
@ -22,6 +23,36 @@ impl<S: 'static> Router<S> {
Router(Rc::new(RouteRecognizer::new(prefix, resources)))
}
pub fn has_route(&self, path: &str) -> bool {
self.0.recognize(path).is_some()
}
pub fn resource_path<'a, U>(&self, prefix: &str, name: &str, elements: U)
-> Result<String, UriGenerationError>
where U: IntoIterator<Item=&'a str>
{
if let Some(pattern) = self.0.get_pattern(name) {
let mut iter = elements.into_iter();
let mut vec = vec![prefix];
for el in pattern.elements() {
match *el {
PatternElement::Str(ref s) => vec.push(s),
PatternElement::Var(_) => {
if let Some(val) = iter.next() {
vec.push(val)
} else {
return Err(UriGenerationError::NotEnoughElements)
}
}
}
}
let s = vec.join("/").to_owned();
Ok(s)
} else {
Err(UriGenerationError::ResourceNotFound)
}
}
}
/// Application

View File

@ -12,8 +12,8 @@
pub use handler::Handler;
pub use pipeline::Pipeline;
pub use channel::{HttpChannel, HttpHandler};
pub use recognizer::{FromParam, RouteRecognizer};
pub use recognizer::{FromParam, RouteRecognizer, Pattern, PatternElement};
pub use cookie::CookieBuilder;
pub use application::ApplicationBuilder;
pub use httpresponse::HttpResponseBuilder;
pub use cookie::CookieBuilder;

View File

@ -403,6 +403,15 @@ impl ResponseError for UriSegmentError {
}
}
/// Errors which can occur when attempting to generate resource uri.
#[derive(Fail, Debug, PartialEq)]
pub enum UriGenerationError {
#[fail(display="Resource not found")]
ResourceNotFound,
#[fail(display="Not all path pattern covered")]
NotEnoughElements,
}
#[cfg(test)]
mod tests {
use std::error::Error as StdError;

View File

@ -38,7 +38,7 @@ impl Default for HttpMessage {
prefix: 0,
version: Version::HTTP_11,
headers: HeaderMap::new(),
params: Params::empty(),
params: Params::default(),
cookies: Vec::new(),
cookies_loaded: false,
addr: None,
@ -64,7 +64,7 @@ impl HttpRequest<()> {
prefix: 0,
version: version,
headers: headers,
params: Params::empty(),
params: Params::default(),
cookies: Vec::new(),
cookies_loaded: false,
addr: None,

View File

@ -32,6 +32,16 @@ pub struct Params {
names: Rc<HashMap<String, usize>>,
}
impl Default for Params {
fn default() -> Params {
Params {
text: String::new(),
names: Rc::new(HashMap::new()),
matches: Vec::new(),
}
}
}
impl Params {
pub(crate) fn new(names: Rc<HashMap<String, usize>>,
text: &str,
@ -47,15 +57,6 @@ impl Params {
}
}
pub(crate) fn empty() -> Self
{
Params {
text: String::new(),
names: Rc::new(HashMap::new()),
matches: Vec::new(),
}
}
/// Check if there are any matched patterns
pub fn is_empty(&self) -> bool {
self.names.is_empty()
@ -202,9 +203,10 @@ FROM_STR!(std::net::SocketAddrV4);
FROM_STR!(std::net::SocketAddrV6);
pub struct RouteRecognizer<T> {
re: RegexSet,
prefix: usize,
patterns: RegexSet,
routes: Vec<(Pattern, T)>,
patterns: HashMap<String, Pattern>,
}
impl<T> Default for RouteRecognizer<T> {
@ -212,8 +214,9 @@ impl<T> Default for RouteRecognizer<T> {
fn default() -> Self {
RouteRecognizer {
prefix: 0,
patterns: RegexSet::new([""].iter()).unwrap(),
re: RegexSet::new([""].iter()).unwrap(),
routes: Vec::new(),
patterns: HashMap::new(),
}
}
}
@ -225,30 +228,28 @@ impl<T> RouteRecognizer<T> {
{
let mut paths = Vec::new();
let mut handlers = Vec::new();
let mut patterns = HashMap::new();
for item in routes {
let (pat, elements) = parse(&item.0);
handlers.push((Pattern::new(&pat, elements), item.2));
let pattern = Pattern::new(&pat, elements);
if let Some(ref name) = item.1 {
let _ = patterns.insert(name.clone(), pattern.clone());
}
handlers.push((pattern, item.2));
paths.push(pat);
};
let regset = RegexSet::new(&paths);
RouteRecognizer {
re: regset.unwrap(),
prefix: prefix.into().len() - 1,
patterns: regset.unwrap(),
routes: handlers,
patterns: patterns,
}
}
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, elements) = parse(item.0);
handlers.push((Pattern::new(&pat, elements), item.2));
paths.push(pat);
};
self.patterns = RegexSet::new(&paths).unwrap();
self.routes = handlers;
pub fn get_pattern(&self, name: &str) -> Option<&Pattern> {
self.patterns.get(name)
}
pub fn set_prefix<P: Into<String>>(&mut self, prefix: P) {
@ -263,11 +264,11 @@ impl<T> RouteRecognizer<T> {
pub fn recognize(&self, path: &str) -> Option<(Option<Params>, &T)> {
let p = &path[self.prefix..];
if p.is_empty() {
if let Some(idx) = self.patterns.matches("/").into_iter().next() {
if let Some(idx) = self.re.matches("/").into_iter().next() {
let (ref pattern, ref route) = self.routes[idx];
return Some((pattern.match_info(&path[self.prefix..]), route))
}
} else if let Some(idx) = self.patterns.matches(p).into_iter().next() {
} else if let Some(idx) = self.re.matches(p).into_iter().next() {
let (ref pattern, ref route) = self.routes[idx];
return Some((pattern.match_info(&path[self.prefix..]), route))
}
@ -275,12 +276,14 @@ impl<T> RouteRecognizer<T> {
}
}
enum PatternElement {
#[derive(Debug, Clone, PartialEq)]
pub enum PatternElement {
Str(String),
Var(String),
}
struct Pattern {
#[derive(Clone)]
pub struct Pattern {
re: Regex,
names: Rc<HashMap<String, usize>>,
elements: Vec<PatternElement>,
@ -309,6 +312,10 @@ impl Pattern {
Some(Params::new(Rc::clone(&self.names), text, &captures))
}
pub fn elements(&self) -> &Vec<PatternElement> {
&self.elements
}
}
pub(crate) fn check_pattern(path: &str) {
@ -337,7 +344,7 @@ fn parse(pattern: &str) -> (String, Vec<PatternElement>) {
if in_param {
// In parameter segment: `{....}`
if ch == '}' {
elems.push(PatternElement::Var(String::from(String::from(param_name.as_str()))));
elems.push(PatternElement::Var(param_name.clone()));
re.push_str(&format!(r"(?P<{}>{})", &param_name, &param_pattern));
param_name.clear();
@ -359,7 +366,7 @@ fn parse(pattern: &str) -> (String, Vec<PatternElement>) {
}
} else if ch == '{' {
in_param = true;
elems.push(PatternElement::Str(String::from(el.as_str())));
elems.push(PatternElement::Str(el.clone()));
el.clear();
} else {
re.push(ch);