mirror of
https://github.com/fafhrd91/actix-web
synced 2025-02-25 13:22:50 +01:00
work on resource_path api
This commit is contained in:
parent
3de43c2a46
commit
d8b880e167
@ -27,7 +27,7 @@ fn index(mut req: HttpRequest) -> Result<HttpResponse> {
|
|||||||
req.session().set("counter", 1)?;
|
req.session().set("counter", 1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().into())
|
Ok("Welcome!".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// async handler
|
/// async handler
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use error::UriGenerationError;
|
||||||
use handler::{Reply, RouteHandler};
|
use handler::{Reply, RouteHandler};
|
||||||
use route::Route;
|
use route::Route;
|
||||||
use resource::Resource;
|
use resource::Resource;
|
||||||
use recognizer::{RouteRecognizer, check_pattern};
|
use recognizer::{RouteRecognizer, check_pattern, PatternElement};
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use channel::HttpHandler;
|
use channel::HttpHandler;
|
||||||
use pipeline::Pipeline;
|
use pipeline::Pipeline;
|
||||||
@ -22,6 +23,36 @@ impl<S: 'static> Router<S> {
|
|||||||
|
|
||||||
Router(Rc::new(RouteRecognizer::new(prefix, resources)))
|
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
|
/// Application
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
pub use handler::Handler;
|
pub use handler::Handler;
|
||||||
pub use pipeline::Pipeline;
|
pub use pipeline::Pipeline;
|
||||||
pub use channel::{HttpChannel, HttpHandler};
|
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 application::ApplicationBuilder;
|
||||||
pub use httpresponse::HttpResponseBuilder;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
@ -38,7 +38,7 @@ impl Default for HttpMessage {
|
|||||||
prefix: 0,
|
prefix: 0,
|
||||||
version: Version::HTTP_11,
|
version: Version::HTTP_11,
|
||||||
headers: HeaderMap::new(),
|
headers: HeaderMap::new(),
|
||||||
params: Params::empty(),
|
params: Params::default(),
|
||||||
cookies: Vec::new(),
|
cookies: Vec::new(),
|
||||||
cookies_loaded: false,
|
cookies_loaded: false,
|
||||||
addr: None,
|
addr: None,
|
||||||
@ -64,7 +64,7 @@ impl HttpRequest<()> {
|
|||||||
prefix: 0,
|
prefix: 0,
|
||||||
version: version,
|
version: version,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
params: Params::empty(),
|
params: Params::default(),
|
||||||
cookies: Vec::new(),
|
cookies: Vec::new(),
|
||||||
cookies_loaded: false,
|
cookies_loaded: false,
|
||||||
addr: None,
|
addr: None,
|
||||||
|
@ -32,6 +32,16 @@ pub struct Params {
|
|||||||
names: Rc<HashMap<String, usize>>,
|
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 {
|
impl Params {
|
||||||
pub(crate) fn new(names: Rc<HashMap<String, usize>>,
|
pub(crate) fn new(names: Rc<HashMap<String, usize>>,
|
||||||
text: &str,
|
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
|
/// Check if there are any matched patterns
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.names.is_empty()
|
self.names.is_empty()
|
||||||
@ -202,9 +203,10 @@ FROM_STR!(std::net::SocketAddrV4);
|
|||||||
FROM_STR!(std::net::SocketAddrV6);
|
FROM_STR!(std::net::SocketAddrV6);
|
||||||
|
|
||||||
pub struct RouteRecognizer<T> {
|
pub struct RouteRecognizer<T> {
|
||||||
|
re: RegexSet,
|
||||||
prefix: usize,
|
prefix: usize,
|
||||||
patterns: RegexSet,
|
|
||||||
routes: Vec<(Pattern, T)>,
|
routes: Vec<(Pattern, T)>,
|
||||||
|
patterns: HashMap<String, Pattern>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for RouteRecognizer<T> {
|
impl<T> Default for RouteRecognizer<T> {
|
||||||
@ -212,8 +214,9 @@ impl<T> Default for RouteRecognizer<T> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
RouteRecognizer {
|
RouteRecognizer {
|
||||||
prefix: 0,
|
prefix: 0,
|
||||||
patterns: RegexSet::new([""].iter()).unwrap(),
|
re: RegexSet::new([""].iter()).unwrap(),
|
||||||
routes: Vec::new(),
|
routes: Vec::new(),
|
||||||
|
patterns: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,30 +228,28 @@ impl<T> RouteRecognizer<T> {
|
|||||||
{
|
{
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
let mut handlers = Vec::new();
|
let mut handlers = Vec::new();
|
||||||
|
let mut patterns = HashMap::new();
|
||||||
for item in routes {
|
for item in routes {
|
||||||
let (pat, elements) = parse(&item.0);
|
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);
|
paths.push(pat);
|
||||||
};
|
};
|
||||||
let regset = RegexSet::new(&paths);
|
let regset = RegexSet::new(&paths);
|
||||||
|
|
||||||
RouteRecognizer {
|
RouteRecognizer {
|
||||||
|
re: regset.unwrap(),
|
||||||
prefix: prefix.into().len() - 1,
|
prefix: prefix.into().len() - 1,
|
||||||
patterns: regset.unwrap(),
|
|
||||||
routes: handlers,
|
routes: handlers,
|
||||||
|
patterns: patterns,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_routes(&mut self, routes: Vec<(&str, Option<&str>, T)>) {
|
pub fn get_pattern(&self, name: &str) -> Option<&Pattern> {
|
||||||
let mut paths = Vec::new();
|
self.patterns.get(name)
|
||||||
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 set_prefix<P: Into<String>>(&mut self, prefix: P) {
|
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)> {
|
pub fn recognize(&self, path: &str) -> Option<(Option<Params>, &T)> {
|
||||||
let p = &path[self.prefix..];
|
let p = &path[self.prefix..];
|
||||||
if p.is_empty() {
|
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];
|
let (ref pattern, ref route) = self.routes[idx];
|
||||||
return Some((pattern.match_info(&path[self.prefix..]), route))
|
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];
|
let (ref pattern, ref route) = self.routes[idx];
|
||||||
return Some((pattern.match_info(&path[self.prefix..]), route))
|
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),
|
Str(String),
|
||||||
Var(String),
|
Var(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Pattern {
|
#[derive(Clone)]
|
||||||
|
pub struct Pattern {
|
||||||
re: Regex,
|
re: Regex,
|
||||||
names: Rc<HashMap<String, usize>>,
|
names: Rc<HashMap<String, usize>>,
|
||||||
elements: Vec<PatternElement>,
|
elements: Vec<PatternElement>,
|
||||||
@ -309,6 +312,10 @@ impl Pattern {
|
|||||||
|
|
||||||
Some(Params::new(Rc::clone(&self.names), text, &captures))
|
Some(Params::new(Rc::clone(&self.names), text, &captures))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn elements(&self) -> &Vec<PatternElement> {
|
||||||
|
&self.elements
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn check_pattern(path: &str) {
|
pub(crate) fn check_pattern(path: &str) {
|
||||||
@ -337,7 +344,7 @@ fn parse(pattern: &str) -> (String, Vec<PatternElement>) {
|
|||||||
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()))));
|
elems.push(PatternElement::Var(param_name.clone()));
|
||||||
re.push_str(&format!(r"(?P<{}>{})", ¶m_name, ¶m_pattern));
|
re.push_str(&format!(r"(?P<{}>{})", ¶m_name, ¶m_pattern));
|
||||||
|
|
||||||
param_name.clear();
|
param_name.clear();
|
||||||
@ -359,7 +366,7 @@ fn parse(pattern: &str) -> (String, Vec<PatternElement>) {
|
|||||||
}
|
}
|
||||||
} else if ch == '{' {
|
} else if ch == '{' {
|
||||||
in_param = true;
|
in_param = true;
|
||||||
elems.push(PatternElement::Str(String::from(el.as_str())));
|
elems.push(PatternElement::Str(el.clone()));
|
||||||
el.clear();
|
el.clear();
|
||||||
} else {
|
} else {
|
||||||
re.push(ch);
|
re.push(ch);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user