From 4a39216aa7a1c232d49e4c34d8f932bf1596f876 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 2 Jun 2018 11:44:09 -0700 Subject: [PATCH] fixed HttpRequest::url_for for a named route with no variables #265 --- src/httprequest.rs | 34 +++++++++++++++++++++--- src/router.rs | 66 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/httprequest.rs b/src/httprequest.rs index 0a14ca043..a618abdcc 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -311,6 +311,7 @@ impl HttpRequest { Err(UrlGenerationError::RouterNotAvailable) } else { let path = self.router().unwrap().resource_path(name, elements)?; + println!("==== {:?}", path); if path.starts_with('/') { let conn = self.connection_info(); Ok(Url::parse(&format!( @@ -325,6 +326,15 @@ impl HttpRequest { } } + /// Generate url for named resource + /// + /// This method is similar to `HttpRequest::url_for()` but it can be used + /// for urls that do not contain variable parts. + pub fn url_for_static(&self, name: &str) -> Result { + const NO_PARAMS: [&str; 0] = []; + self.url_for(name, &NO_PARAMS) + } + /// This method returns reference to current `Router` object. #[inline] pub fn router(&self) -> Option<&Router> { @@ -695,20 +705,38 @@ mod tests { let mut resource = ResourceHandler::<()>::default(); resource.name("index"); - let routes = - vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))]; + let routes = vec![(Resource::new("index", "/user/{name}.html"), 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"]); + let url = req.url_for("index", &["test"]); assert_eq!( url.ok().unwrap().as_str(), "http://www.rust-lang.org/prefix/user/test.html" ); } + #[test] + fn test_url_for_static() { + let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish(); + + let mut resource = ResourceHandler::<()>::default(); + resource.name("index"); + let routes = vec![(Resource::new("index", "/index.html"), Some(resource))]; + let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); + assert!(router.has_route("/index.html")); + assert!(!router.has_route("/prefix/index.html")); + + let req = req.with_state(Rc::new(()), router); + let url = req.url_for_static("index"); + assert_eq!( + url.ok().unwrap().as_str(), + "http://www.rust-lang.org/prefix/index.html" + ); + } + #[test] fn test_url_for_external() { let req = HttpRequest::default(); diff --git a/src/router.rs b/src/router.rs index f65e31559..c2ce4f546 100644 --- a/src/router.rs +++ b/src/router.rs @@ -211,6 +211,8 @@ impl Resource { let (pattern, elements, is_dynamic, len) = Resource::parse(path, prefix, for_prefix); + println!("ELEMENT: {:?} {:?} {:?}", pattern, elements, is_dynamic); + let tp = if is_dynamic { let re = match Regex::new(&pattern) { Ok(re) => re, @@ -338,22 +340,42 @@ impl Resource { U: IntoIterator, I: AsRef, { - let mut iter = elements.into_iter(); - let mut path = if self.rtp != ResourceType::External { - format!("{}/", router.prefix()) - } else { - String::new() - }; - for el in &self.elements { - match *el { - PatternElement::Str(ref s) => path.push_str(s), - PatternElement::Var(_) => { - if let Some(val) = iter.next() { - path.push_str(val.as_ref()) - } else { - return Err(UrlGenerationError::NotEnoughElements); + let mut path = match self.tp { + PatternType::Prefix(ref p) => p.to_owned(), + PatternType::Static(ref p) => p.to_owned(), + PatternType::Dynamic(..) => { + let mut path = String::new(); + let mut iter = elements.into_iter(); + for el in &self.elements { + println!("EL: {:?}", el); + match *el { + PatternElement::Str(ref s) => path.push_str(s), + PatternElement::Var(_) => { + if let Some(val) = iter.next() { + path.push_str(val.as_ref()) + } else { + return Err(UrlGenerationError::NotEnoughElements); + } + } } } + path + } + }; + + if self.rtp != ResourceType::External { + let prefix = router.prefix(); + if prefix.ends_with('/') { + if path.starts_with('/') { + path.insert_str(0, &prefix[..prefix.len() - 1]); + } else { + path.insert_str(0, prefix); + } + } else { + if !path.starts_with('/') { + path.insert(0, '/'); + } + path.insert_str(0, prefix); } } Ok(path) @@ -418,6 +440,10 @@ impl Resource { } } + if !el.is_empty() { + elems.push(PatternElement::Str(el.clone())); + } + let re = if is_dynamic { if !for_prefix { re1.push('$'); @@ -450,7 +476,7 @@ mod tests { use test::TestRequest; #[test] - fn test_recognizer() { + fn test_recognizer10() { let routes = vec![ (Resource::new("", "/name"), Some(ResourceHandler::default())), ( @@ -473,6 +499,10 @@ mod tests { Resource::new("", "/v/{tail:.*}"), Some(ResourceHandler::default()), ), + ( + Resource::new("", "/test2/{test}.html"), + Some(ResourceHandler::default()), + ), ( Resource::new("", "{test}/index.html"), Some(ResourceHandler::default()), @@ -510,8 +540,12 @@ mod tests { "blah-blah/index.html" ); - let mut req = TestRequest::with_uri("/bbb/index.html").finish(); + let mut req = TestRequest::with_uri("/test2/index.html").finish(); assert_eq!(rec.recognize(&mut req), Some(6)); + assert_eq!(req.match_info().get("test").unwrap(), "index"); + + let mut req = TestRequest::with_uri("/bbb/index.html").finish(); + assert_eq!(rec.recognize(&mut req), Some(7)); assert_eq!(req.match_info().get("test").unwrap(), "bbb"); }