From 17c27ef42d3fb0dad1e13787d9fcdc53c489530e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 1 Apr 2018 17:37:22 -0700 Subject: [PATCH] HttpRequest::resource() returns current matched resource --- CHANGES.md | 2 + Cargo.toml | 1 + examples/diesel/src/main.rs | 10 +-- src/application.rs | 29 +++---- src/de.rs | 12 +-- src/httprequest.rs | 50 +++++++++--- src/lib.rs | 8 +- src/middleware/cors.rs | 8 +- src/resource.rs | 24 +++--- src/router.rs | 149 ++++++++++++++++++++++++------------ src/test.rs | 4 +- 11 files changed, 191 insertions(+), 106 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 47264c7a4..80a945f66 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,8 @@ * Use more ergonomic `actix_web::Error` instead of `http::Error` for `ClientRequestBuilder::body()` +* Add `HttpRequest::resource()`, returns current matched resource + * Router cannot parse Non-ASCII characters in URL #137 * Fix long client urls #129 diff --git a/Cargo.toml b/Cargo.toml index 6b91f1b39..e5a17e9ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ smallvec = "0.6" time = "0.1" encoding = "0.2" language-tags = "0.2" +lazy_static = "1.0" url = { version="1.7", features=["query_encoding"] } cookie = { version="0.10", features=["percent-encode"] } brotli2 = { version="^0.3.2", optional = true } diff --git a/examples/diesel/src/main.rs b/examples/diesel/src/main.rs index d71880fca..d52a6192a 100644 --- a/examples/diesel/src/main.rs +++ b/examples/diesel/src/main.rs @@ -34,16 +34,14 @@ use db::{CreateUser, DbExecutor}; /// State with DbExecutor address -struct State { +struct App { db: Addr, } /// Async request handler -fn index(req: HttpRequest) -> Box> { - let name = &req.match_info()["name"]; - +fn index(name: Path<(String,)>, state: State) -> FutureResponse { // send async `CreateUser` message to a `DbExecutor` - req.state().db.send(CreateUser{name: name.to_owned()}) + state.db.send(CreateUser{name: name.into_inner()}) .from_err() .and_then(|res| { match res { @@ -72,7 +70,7 @@ fn main() { App::with_state(State{db: addr.clone()}) // enable logger .middleware(middleware::Logger::default()) - .resource("/{name}", |r| r.method(http::Method::GET).a(index))}) + .resource("/{name}", |r| r.method(http::Method::GET).with2(index))}) .bind("127.0.0.1:8080").unwrap() .start(); diff --git a/src/application.rs b/src/application.rs index ad232a798..cf58cc971 100644 --- a/src/application.rs +++ b/src/application.rs @@ -4,8 +4,8 @@ use std::cell::RefCell; use std::collections::HashMap; use handler::Reply; -use router::{Router, Pattern}; -use resource::Resource; +use router::{Router, Resource}; +use resource::{ResourceHandler}; use header::ContentEncoding; use handler::{Handler, RouteHandler, WrapHandler}; use httprequest::HttpRequest; @@ -27,10 +27,10 @@ pub struct HttpApplication { pub(crate) struct Inner { prefix: usize, - default: Resource, + default: ResourceHandler, encoding: ContentEncoding, router: Router, - resources: Vec>, + resources: Vec>, handlers: Vec<(String, Box>)>, } @@ -103,10 +103,10 @@ struct ApplicationParts { state: S, prefix: String, settings: ServerSettings, - default: Resource, - resources: Vec<(Pattern, Option>)>, + default: ResourceHandler, + resources: Vec<(Resource, Option>)>, handlers: Vec<(String, Box>)>, - external: HashMap, + external: HashMap, encoding: ContentEncoding, middlewares: Vec>>, } @@ -126,7 +126,7 @@ impl App<()> { state: (), prefix: "/".to_owned(), settings: ServerSettings::default(), - default: Resource::default_not_found(), + default: ResourceHandler::default_not_found(), resources: Vec::new(), handlers: Vec::new(), external: HashMap::new(), @@ -156,7 +156,7 @@ impl App where S: 'static { state, prefix: "/".to_owned(), settings: ServerSettings::default(), - default: Resource::default_not_found(), + default: ResourceHandler::default_not_found(), resources: Vec::new(), handlers: Vec::new(), external: HashMap::new(), @@ -236,16 +236,16 @@ impl App where S: 'static { /// } /// ``` pub fn resource(mut self, path: &str, f: F) -> App - where F: FnOnce(&mut Resource) + 'static + where F: FnOnce(&mut ResourceHandler) + 'static { { let parts = self.parts.as_mut().expect("Use after finish"); // add resource - let mut resource = Resource::default(); + let mut resource = ResourceHandler::default(); f(&mut resource); - let pattern = Pattern::new(resource.get_name(), path); + let pattern = Resource::new(resource.get_name(), path); parts.resources.push((pattern, Some(resource))); } self @@ -253,7 +253,7 @@ impl App where S: 'static { /// Default resource is used if no matched route could be found. pub fn default_resource(mut self, f: F) -> App - where F: FnOnce(&mut Resource) + 'static + where F: FnOnce(&mut ResourceHandler) + 'static { { let parts = self.parts.as_mut().expect("Use after finish"); @@ -305,7 +305,8 @@ impl App where S: 'static { panic!("External resource {:?} is registered.", name.as_ref()); } parts.external.insert( - String::from(name.as_ref()), Pattern::new(name.as_ref(), url.as_ref())); + String::from(name.as_ref()), + Resource::external(name.as_ref(), url.as_ref())); } self } diff --git a/src/de.rs b/src/de.rs index 314b6779e..a72d6b5b4 100644 --- a/src/de.rs +++ b/src/de.rs @@ -554,8 +554,8 @@ impl<'de> de::VariantAccess<'de> for UnitVariant { mod tests { use futures::{Async, Future}; use super::*; - use router::{Router, Pattern}; - use resource::Resource; + use router::{Router, Resource}; + use resource::ResourceHandler; use test::TestRequest; use server::ServerSettings; @@ -580,10 +580,10 @@ mod tests { fn test_request_extract() { let mut req = TestRequest::with_uri("/name/user1/?id=test").finish(); - let mut resource = Resource::<()>::default(); + let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let mut routes = Vec::new(); - routes.push((Pattern::new("index", "/{key}/{value}/"), Some(resource))); + routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource))); let (router, _) = Router::new("", ServerSettings::default(), routes); assert!(router.recognize(&mut req).is_some()); @@ -639,10 +639,10 @@ mod tests { #[test] fn test_extract_path_signle() { - let mut resource = Resource::<()>::default(); + let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let mut routes = Vec::new(); - routes.push((Pattern::new("index", "/{value}/"), Some(resource))); + routes.push((Resource::new("index", "/{value}/"), Some(resource))); let (router, _) = Router::new("", ServerSettings::default(), routes); let mut req = TestRequest::with_uri("/32/").finish(); diff --git a/src/httprequest.rs b/src/httprequest.rs index 4efc1f340..16dea0c8b 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -17,7 +17,7 @@ use percent_encoding::percent_decode; use body::Body; use info::ConnectionInfo; use param::Params; -use router::Router; +use router::{Router, Resource}; use payload::Payload; use handler::FromRequest; use httpmessage::HttpMessage; @@ -39,6 +39,7 @@ pub struct HttpInnerMessage { pub addr: Option, pub payload: Option, pub info: Option>, + pub resource: i16, } impl Default for HttpInnerMessage { @@ -57,6 +58,7 @@ impl Default for HttpInnerMessage { payload: None, extensions: Extensions::new(), info: None, + resource: -1, } } } @@ -93,9 +95,15 @@ impl HttpInnerMessage { self.addr = None; self.info = None; self.payload = None; + self.resource = -1; } } +lazy_static!{ + static ref RESOURCE: Resource = Resource::default(); +} + + /// An HTTP Request pub struct HttpRequest(SharedHttpInnerMessage, Option>, Option); @@ -120,6 +128,7 @@ impl HttpRequest<()> { addr: None, extensions: Extensions::new(), info: None, + resource: -1, }), None, None, @@ -318,6 +327,22 @@ impl HttpRequest { self.2.as_ref() } + /// This method returns reference to matched `Resource` object. + #[inline] + pub fn resource(&self) -> &Resource { + let idx = self.as_ref().resource; + if idx >= 0 { + if let Some(ref router) = self.2 { + return router.get_resource(idx as usize) + } + } + &*RESOURCE + } + + pub(crate) fn set_resource(&mut self, idx: usize) { + self.as_mut().resource = idx as i16; + } + /// Peer socket address /// /// Peer address is actual socket address, if proxy is used in front of @@ -544,8 +569,8 @@ impl fmt::Debug for HttpRequest { mod tests { use super::*; use http::{Uri, HttpTryFrom}; - use router::Pattern; - use resource::Resource; + use router::Resource; + use resource::ResourceHandler; use test::TestRequest; use server::ServerSettings; @@ -607,10 +632,10 @@ mod tests { fn test_request_match_info() { let mut req = TestRequest::with_uri("/value/?id=test").finish(); - let mut resource = Resource::<()>::default(); + let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let mut routes = Vec::new(); - routes.push((Pattern::new("index", "/{key}/"), Some(resource))); + routes.push((Resource::new("index", "/{key}/"), Some(resource))); let (router, _) = Router::new("", ServerSettings::default(), routes); assert!(router.recognize(&mut req).is_some()); @@ -623,9 +648,9 @@ mod tests { assert_eq!(req2.url_for("unknown", &["test"]), Err(UrlGenerationError::RouterNotAvailable)); - let mut resource = Resource::<()>::default(); + let mut resource = ResourceHandler::<()>::default(); resource.name("index"); - let routes = vec!((Pattern::new("index", "/user/{name}.{ext}"), Some(resource))); + let routes = vec!((Resource::new("index", "/user/{name}.{ext}"), Some(resource))); let (router, _) = Router::new("/", ServerSettings::default(), routes); assert!(router.has_route("/user/test.html")); assert!(!router.has_route("/test/unknown")); @@ -645,26 +670,27 @@ mod tests { fn test_url_for_with_prefix() { let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish(); - let mut resource = Resource::<()>::default(); + let mut resource = ResourceHandler::<()>::default(); resource.name("index"); - let routes = vec![(Pattern::new("index", "/user/{name}.{ext}"), Some(resource))]; + let routes = vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))]; let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); assert!(router.has_route("/user/test.html")); assert!(!router.has_route("/prefix/user/test.html")); let req = req.with_state(Rc::new(()), router); let url = req.url_for("index", &["test", "html"]); - assert_eq!(url.ok().unwrap().as_str(), "http://www.rust-lang.org/prefix/user/test.html"); + assert_eq!(url.ok().unwrap().as_str(), + "http://www.rust-lang.org/prefix/user/test.html"); } #[test] fn test_url_for_external() { let req = HttpRequest::default(); - let mut resource = Resource::<()>::default(); + let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let routes = vec![ - (Pattern::new("youtube", "https://youtube.com/watch/{video_id}"), None)]; + (Resource::external("youtube", "https://youtube.com/watch/{video_id}"), None)]; let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); assert!(!router.has_route("https://youtube.com/watch/unknown")); diff --git a/src/lib.rs b/src/lib.rs index 555f610fd..6f202ebbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,8 @@ extern crate bitflags; #[macro_use] extern crate failure; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate futures; extern crate futures_cpupool; extern crate tokio_io; @@ -174,12 +176,12 @@ pub mod dev { pub use body::BodyStream; pub use context::Drain; + pub use json::JsonBody; pub use info::ConnectionInfo; pub use handler::{Handler, Reply, FromRequest}; pub use route::Route; - pub use resource::Resource; - pub use json::JsonBody; - pub use router::{Router, Pattern}; + pub use router::{Router, Resource}; + pub use resource::ResourceHandler; pub use param::{FromParam, Params}; pub use httpmessage::{UrlEncoded, MessageBody}; pub use httpresponse::HttpResponseBuilder; diff --git a/src/middleware/cors.rs b/src/middleware/cors.rs index bfbf54a24..28c5c7898 100644 --- a/src/middleware/cors.rs +++ b/src/middleware/cors.rs @@ -10,7 +10,7 @@ //! 3. Call [finish](struct.Cors.html#method.finish) to retrieve the constructed backend. //! //! Cors middleware could be used as parameter for `App::middleware()` or -//! `Resource::middleware()` methods. But you have to use `Cors::register()` method to +//! `ResourceHandler::middleware()` methods. But you have to use `Cors::register()` method to //! support *preflight* OPTIONS request. //! //! @@ -52,7 +52,7 @@ use http::{self, Method, HttpTryFrom, Uri, StatusCode}; use http::header::{self, HeaderName, HeaderValue}; use error::{Result, ResponseError}; -use resource::Resource; +use resource::ResourceHandler; use httpmessage::HttpMessage; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -212,10 +212,10 @@ impl Cors { /// This method register cors middleware with resource and /// adds route for *OPTIONS* preflight requests. /// - /// It is possible to register *Cors* middleware with `Resource::middleware()` + /// It is possible to register *Cors* middleware with `ResourceHandler::middleware()` /// method, but in that case *Cors* middleware wont be able to handle *OPTIONS* /// requests. - pub fn register(self, resource: &mut Resource) { + pub fn register(self, resource: &mut ResourceHandler) { resource.method(Method::OPTIONS).h(|_| HttpResponse::Ok()); resource.middleware(self); } diff --git a/src/resource.rs b/src/resource.rs index da74f4e0e..f28363e28 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -31,16 +31,16 @@ use httpresponse::HttpResponse; /// "/", |r| r.method(http::Method::GET).f(|r| HttpResponse::Ok())) /// .finish(); /// } -pub struct Resource { +pub struct ResourceHandler { name: String, state: PhantomData, routes: SmallVec<[Route; 3]>, middlewares: Rc>>>, } -impl Default for Resource { +impl Default for ResourceHandler { fn default() -> Self { - Resource { + ResourceHandler { name: String::new(), state: PhantomData, routes: SmallVec::new(), @@ -48,10 +48,10 @@ impl Default for Resource { } } -impl Resource { +impl ResourceHandler { pub(crate) fn default_not_found() -> Self { - Resource { + ResourceHandler { name: String::new(), state: PhantomData, routes: SmallVec::new(), @@ -68,7 +68,7 @@ impl Resource { } } -impl Resource { +impl ResourceHandler { /// Register a new route and return mutable reference to *Route* object. /// *Route* is used for route configuration, i.e. adding predicates, setting up handler. @@ -97,7 +97,7 @@ impl Resource { /// This is shortcut for: /// /// ```rust,ignore - /// Resource::resource("/", |r| r.route().filter(pred::Get()).f(index) + /// Application::resource("/", |r| r.route().filter(pred::Get()).f(index) /// ``` pub fn method(&mut self, method: Method) -> &mut Route { self.routes.push(Route::default()); @@ -109,7 +109,7 @@ impl Resource { /// This is shortcut for: /// /// ```rust,ignore - /// Resource::resource("/", |r| r.route().h(handler) + /// Application::resource("/", |r| r.route().h(handler) /// ``` pub fn h>(&mut self, handler: H) { self.routes.push(Route::default()); @@ -121,7 +121,7 @@ impl Resource { /// This is shortcut for: /// /// ```rust,ignore - /// Resource::resource("/", |r| r.route().f(index) + /// Application::resource("/", |r| r.route().f(index) /// ``` pub fn f(&mut self, handler: F) where F: Fn(HttpRequest) -> R + 'static, @@ -136,7 +136,7 @@ impl Resource { /// This is shortcut for: /// /// ```rust,ignore - /// Resource::resource("/", |r| r.route().with(index) + /// Application::resource("/", |r| r.route().with(index) /// ``` pub fn with(&mut self, handler: F) where F: Fn(T) -> R + 'static, @@ -147,7 +147,7 @@ impl Resource { self.routes.last_mut().unwrap().with(handler) } - /// Register a middleware + /// Register a resource middleware /// /// This is similar to `App's` middlewares, but /// middlewares get invoked on resource level. @@ -157,7 +157,7 @@ impl Resource { pub(crate) fn handle(&mut self, mut req: HttpRequest, - default: Option<&mut Resource>) -> Reply + default: Option<&mut ResourceHandler>) -> Reply { for route in &mut self.routes { if route.check(&mut req) { diff --git a/src/router.rs b/src/router.rs index 57b4d0a12..050520b20 100644 --- a/src/router.rs +++ b/src/router.rs @@ -6,9 +6,9 @@ use std::collections::HashMap; use regex::{Regex, escape}; use percent_encoding::percent_decode; -use error::UrlGenerationError; use param::Params; -use resource::Resource; +use error::UrlGenerationError; +use resource::ResourceHandler; use httprequest::HttpRequest; use server::ServerSettings; @@ -19,8 +19,8 @@ pub struct Router(Rc); struct Inner { prefix: String, prefix_len: usize, - named: HashMap, - patterns: Vec, + named: HashMap, + patterns: Vec, srv: ServerSettings, } @@ -28,7 +28,8 @@ impl Router { /// Create new router pub fn new(prefix: &str, settings: ServerSettings, - map: Vec<(Pattern, Option>)>) -> (Router, Vec>) + map: Vec<(Resource, Option>)>) + -> (Router, Vec>) { let prefix = prefix.trim().trim_right_matches('/').to_owned(); let mut named = HashMap::new(); @@ -64,6 +65,10 @@ impl Router { &self.0.srv } + pub(crate) fn get_resource(&self, idx: usize) -> &Resource { + &self.0.patterns[idx] + } + /// Query for matched resource pub fn recognize(&self, req: &mut HttpRequest) -> Option { if self.0.prefix_len > req.path().len() { @@ -75,6 +80,7 @@ impl Router { for (idx, pattern) in self.0.patterns.iter().enumerate() { if pattern.match_with_params(p.as_ref(), req.match_info_mut()) { + req.set_resource(idx); return Some(idx) } } @@ -108,11 +114,7 @@ impl Router { I: AsRef, { if let Some(pattern) = self.0.named.get(name) { - if pattern.1 { - pattern.0.path(None, elements) - } else { - pattern.0.path(Some(&self.0.prefix), elements) - } + pattern.0.resource_path(self, elements) } else { Err(UrlGenerationError::ResourceNotFound) } @@ -125,6 +127,7 @@ impl Clone for Router { } } + #[derive(Debug, Clone, PartialEq)] enum PatternElement { Str(String), @@ -137,25 +140,48 @@ enum PatternType { Dynamic(Regex, Vec), } +/// Reslource type describes an entry in resources table #[derive(Clone)] -pub struct Pattern { +pub struct Resource { tp: PatternType, name: String, pattern: String, elements: Vec, + external: bool, } -impl Pattern { - /// Parse path pattern and create new `Pattern` instance. +impl Default for Resource { + fn default() -> Resource { + Resource { + tp: PatternType::Static("".to_owned()), + name: "".to_owned(), + pattern: "".to_owned(), + elements: Vec::new(), + external: false, + } + } +} + +impl Resource { + /// Parse path pattern and create new `Resource` instance. /// /// Panics if path pattern is wrong. pub fn new(name: &str, path: &str) -> Self { - Pattern::with_prefix(name, path, "/") + Resource::with_prefix(name, path, "/") } - /// Parse path pattern and create new `Pattern` instance with custom prefix + /// Construct external resource + /// + /// Panics if path pattern is wrong. + pub fn external(name: &str, path: &str) -> Self { + let mut resource = Resource::with_prefix(name, path, "/"); + resource.external = true; + resource + } + + /// Parse path pattern and create new `Resource` instance with custom prefix pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self { - let (pattern, elements, is_dynamic) = Pattern::parse(path, prefix); + let (pattern, elements, is_dynamic) = Resource::parse(path, prefix); let tp = if is_dynamic { let re = match Regex::new(&pattern) { @@ -170,20 +196,21 @@ impl Pattern { PatternType::Static(pattern.clone()) }; - Pattern { + Resource { tp, - pattern, elements, name: name.into(), + pattern: path.to_owned(), + external: false, } } - /// Returns name of the pattern + /// Name of the resource pub fn name(&self) -> &str { &self.name } - /// Returns path of the pattern + /// Path pattern of the resource pub fn pattern(&self) -> &str { &self.pattern } @@ -219,14 +246,15 @@ impl Pattern { } } - /// Build pattern path. - pub fn path(&self, prefix: Option<&str>, elements: U) -> Result + /// Build reousrce path. + pub fn resource_path(&self, router: &Router, elements: U) + -> Result where U: IntoIterator, I: AsRef, { let mut iter = elements.into_iter(); - let mut path = if let Some(prefix) = prefix { - format!("{}/", prefix) + let mut path = if !self.external { + format!("{}/", router.prefix()) } else { String::new() }; @@ -309,15 +337,15 @@ impl Pattern { } } -impl PartialEq for Pattern { - fn eq(&self, other: &Pattern) -> bool { +impl PartialEq for Resource { + fn eq(&self, other: &Resource) -> bool { self.pattern == other.pattern } } -impl Eq for Pattern {} +impl Eq for Resource {} -impl Hash for Pattern { +impl Hash for Resource { fn hash(&self, state: &mut H) { self.pattern.hash(state); } @@ -331,13 +359,20 @@ mod tests { #[test] fn test_recognizer() { let routes = vec![ - (Pattern::new("", "/name"), Some(Resource::default())), - (Pattern::new("", "/name/{val}"), Some(Resource::default())), - (Pattern::new("", "/name/{val}/index.html"), Some(Resource::default())), - (Pattern::new("", "/file/{file}.{ext}"), Some(Resource::default())), - (Pattern::new("", "/v{val}/{val2}/index.html"), Some(Resource::default())), - (Pattern::new("", "/v/{tail:.*}"), Some(Resource::default())), - (Pattern::new("", "{test}/index.html"), Some(Resource::default()))]; + (Resource::new("", "/name"), + Some(ResourceHandler::default())), + (Resource::new("", "/name/{val}"), + Some(ResourceHandler::default())), + (Resource::new("", "/name/{val}/index.html"), + Some(ResourceHandler::default())), + (Resource::new("", "/file/{file}.{ext}"), + Some(ResourceHandler::default())), + (Resource::new("", "/v{val}/{val2}/index.html"), + Some(ResourceHandler::default())), + (Resource::new("", "/v/{tail:.*}"), + Some(ResourceHandler::default())), + (Resource::new("", "{test}/index.html"), + Some(ResourceHandler::default()))]; let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); let mut req = TestRequest::with_uri("/name").finish(); @@ -375,8 +410,8 @@ mod tests { #[test] fn test_recognizer_2() { let routes = vec![ - (Pattern::new("", "/index.json"), Some(Resource::default())), - (Pattern::new("", "/{source}.json"), Some(Resource::default()))]; + (Resource::new("", "/index.json"), Some(ResourceHandler::default())), + (Resource::new("", "/{source}.json"), Some(ResourceHandler::default()))]; let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); let mut req = TestRequest::with_uri("/index.json").finish(); @@ -389,8 +424,8 @@ mod tests { #[test] fn test_recognizer_with_prefix() { let routes = vec![ - (Pattern::new("", "/name"), Some(Resource::default())), - (Pattern::new("", "/name/{val}"), Some(Resource::default()))]; + (Resource::new("", "/name"), Some(ResourceHandler::default())), + (Resource::new("", "/name/{val}"), Some(ResourceHandler::default()))]; let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes); let mut req = TestRequest::with_uri("/name").finish(); @@ -406,8 +441,8 @@ mod tests { // same patterns let routes = vec![ - (Pattern::new("", "/name"), Some(Resource::default())), - (Pattern::new("", "/name/{val}"), Some(Resource::default()))]; + (Resource::new("", "/name"), Some(ResourceHandler::default())), + (Resource::new("", "/name/{val}"), Some(ResourceHandler::default()))]; let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes); let mut req = TestRequest::with_uri("/name").finish(); @@ -423,22 +458,22 @@ mod tests { #[test] fn test_parse_static() { - let re = Pattern::new("test", "/"); + let re = Resource::new("test", "/"); assert!(re.is_match("/")); assert!(!re.is_match("/a")); - let re = Pattern::new("test", "/name"); + let re = Resource::new("test", "/name"); assert!(re.is_match("/name")); assert!(!re.is_match("/name1")); assert!(!re.is_match("/name/")); assert!(!re.is_match("/name~")); - let re = Pattern::new("test", "/name/"); + let re = Resource::new("test", "/name/"); assert!(re.is_match("/name/")); assert!(!re.is_match("/name")); assert!(!re.is_match("/name/gs")); - let re = Pattern::new("test", "/user/profile"); + let re = Resource::new("test", "/user/profile"); assert!(re.is_match("/user/profile")); assert!(!re.is_match("/user/profile/profile")); } @@ -447,7 +482,7 @@ mod tests { fn test_parse_param() { let mut req = HttpRequest::default(); - let re = Pattern::new("test", "/user/{id}"); + let re = Resource::new("test", "/user/{id}"); assert!(re.is_match("/user/profile")); assert!(re.is_match("/user/2345")); assert!(!re.is_match("/user/2345/")); @@ -461,7 +496,7 @@ mod tests { assert!(re.match_with_params("/user/1245125", req.match_info_mut())); assert_eq!(req.match_info().get("id").unwrap(), "1245125"); - let re = Pattern::new("test", "/v{version}/resource/{id}"); + let re = Resource::new("test", "/v{version}/resource/{id}"); assert!(re.is_match("/v1/resource/320120")); assert!(!re.is_match("/v/resource/1")); assert!(!re.is_match("/resource")); @@ -471,4 +506,24 @@ mod tests { assert_eq!(req.match_info().get("version").unwrap(), "151"); assert_eq!(req.match_info().get("id").unwrap(), "adahg32"); } + + #[test] + fn test_request_resource() { + let routes = vec![ + (Resource::new("r1", "/index.json"), Some(ResourceHandler::default())), + (Resource::new("r2", "/test.json"), Some(ResourceHandler::default()))]; + let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); + + let mut req = TestRequest::with_uri("/index.json") + .finish_with_router(router.clone()); + assert_eq!(router.recognize(&mut req), Some(0)); + let resource = req.resource(); + assert_eq!(resource.name(), "r1"); + + let mut req = TestRequest::with_uri("/test.json") + .finish_with_router(router.clone()); + assert_eq!(router.recognize(&mut req), Some(1)); + let resource = req.resource(); + assert_eq!(resource.name(), "r2"); + } } diff --git a/src/test.rs b/src/test.rs index 742330915..b6fd22d2c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -27,7 +27,7 @@ use application::{App, HttpApplication}; use param::Params; use router::Router; use payload::Payload; -use resource::Resource; +use resource::ResourceHandler; use httprequest::HttpRequest; use httpresponse::HttpResponse; use server::{HttpServer, IntoHttpHandler, ServerSettings}; @@ -352,7 +352,7 @@ impl TestApp { /// Register resource. This method is similar /// to `App::resource()` method. pub fn resource(&mut self, path: &str, f: F) -> &mut TestApp - where F: FnOnce(&mut Resource) + 'static + where F: FnOnce(&mut ResourceHandler) + 'static { self.app = Some(self.app.take().unwrap().resource(path, f)); self