mirror of
https://github.com/actix/actix-web.git
synced 2024-12-27 02:20:33 +00:00
work on resource_path api
This commit is contained in:
parent
3de43c2a46
commit
d8b880e167
6 changed files with 82 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<{}>{})", ¶m_name, ¶m_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);
|
||||
|
|
Loading…
Reference in a new issue