From c696855bc4c6002f020a262e011acf3b3ca4d205 Mon Sep 17 00:00:00 2001 From: Markus Kohlhase Date: Fri, 25 Nov 2016 16:23:31 +0100 Subject: [PATCH] support url generation for mounted routers --- .travis.yml | 16 ++++++++++++++- Cargo.toml | 1 + src/lib.rs | 3 +++ src/url_for.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a6dcc0..52dc06e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,22 @@ rust: - stable - beta sudo: false + +# load travis-cargo +before_script: + - | + pip install 'travis-cargo<0.2' --user && + export PATH=$HOME/.local/bin:$PATH + +script: + - | + travis-cargo build -- --features mount && + travis-cargo test -- --features mount && + travis-cargo --only stable doc + env: global: - secure: MjdqCPMV5iaByoY3m545Iw/S7UHbX5d3ZgU9H3c62Vo2WqJ12EWk4H0Z0HU+Ptu1+FriplfbnKKcfiE9zHam2yBs2XfhysWm/nOCk5eTetSpapYddWkwQaoEgxcan4kUyJiG0v4ZylorZea7d2wa9iKZKFQPChnvCI+Xd/9dlJs= + - TRAVIS_CARGO_NIGHTLY_FEATURE="" + - secure: MjdqCPMV5iaByoY3m545Iw/S7UHbX5d3ZgU9H3c62Vo2WqJ12EWk4H0Z0HU+Ptu1+FriplfbnKKcfiE9zHam2yBs2XfhysWm/nOCk5eTetSpapYddWkwQaoEgxcan4kUyJiG0v4ZylorZea7d2wa9iKZKFQPChnvCI+Xd/9dlJs= after_success: 'curl https://raw.githubusercontent.com/iron-bot/build-doc/master/build-doc.sh | sh ' diff --git a/Cargo.toml b/Cargo.toml index 440412e..1231be8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ keywords = ["iron", "web", "http", "routing", "router"] route-recognizer = "0.1" iron = "0.4" url = "1.1" +mount = { version = "0.2", optional = true } diff --git a/src/lib.rs b/src/lib.rs index c8b6d16..f54b4c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,10 +6,13 @@ extern crate iron; extern crate route_recognizer as recognizer; extern crate url; +#[cfg(feature = "mount")] +extern crate mount; pub use router::{Router, RouterError, NoRoute, TrailingSlash}; pub use recognizer::Params; pub use url_for::url_for; +pub use url_for::mounted_url_for; mod router; mod macros; diff --git a/src/url_for.rs b/src/url_for.rs index fd23d67..3fe63bc 100644 --- a/src/url_for.rs +++ b/src/url_for.rs @@ -4,6 +4,8 @@ use url::Url; use iron::prelude::*; use router::RouterInner; +#[cfg(feature = "mount")] +use mount::OriginalUrl; /// Generate a URL based off of the currently requested URL. /// @@ -12,19 +14,48 @@ use router::RouterInner; /// `params` will be inserted as route parameters if fitting, the rest will be appended as query /// parameters. pub fn url_for(request: &Request, route_id: &str, params: HashMap) -> ::iron::Url { + let url = request.url.clone().into_generic_url(); + _url_for(url, request, route_id, params) +} + +/// Generate a URL based off of the currently requested URL. +/// In contrast to `url_for` this method respects the hidden path if the router is mounted. +/// +/// The `route_id` used during route registration will be used here again. +/// +/// `params` will be inserted as route parameters if fitting, the rest will be appended as query +/// parameters. +#[cfg(feature = "mount")] +pub fn mounted_url_for(request: &Request, route_id: &str, params: HashMap) -> ::iron::Url { + let url = request.extensions.get::().unwrap().clone().into_generic_url(); + _url_for(url, request, route_id, params) +} + +fn _url_for(mut url: Url, request: &Request, route_id: &str, params: HashMap) -> ::iron::Url { let inner = request.extensions.get::().expect("Couldn\'t find router set up properly."); let glob = inner.route_ids.get(route_id).expect("No route with that ID"); - let mut url = request.url.clone().into_generic_url(); url_for_impl(&mut url, glob, params); ::iron::Url::from_generic_url(url).unwrap() } fn url_for_impl(url: &mut Url, glob: &str, mut params: HashMap) { { + let globs = glob.split('/'); + let globs_count = globs.clone().filter(|x| *x != "").count(); + let segments_count = url.path_segments().unwrap().count(); let mut url_path_segments = url.path_segments_mut().unwrap(); - url_path_segments.clear(); - for path_segment in glob.split('/') { + + if globs_count < segments_count { + for _ in 0..globs_count+1 { + url_path_segments.pop(); + } + } else { + url_path_segments.clear(); + } + + let mut idx = 0; + for path_segment in globs { if path_segment.len() > 1 && (path_segment.starts_with(':') || path_segment.starts_with('*')) { let key = &path_segment[1..]; match params.remove(key) { @@ -32,7 +63,11 @@ fn url_for_impl(url: &mut Url, glob: &str, mut params: HashMap) None => panic!("No value for key {}", key) }; } else { - url_path_segments.push(path_segment); + if idx == 0 && path_segment == "" { + idx += 1; + } else { + url_path_segments.push(path_segment); + } } } } @@ -73,4 +108,15 @@ mod test { }); assert_eq!(url.to_string(), "http://localhost/foo/bam/"); } + + #[test] + fn test_with_mount() { + let mut url = "http://localhost/mounted/foo/bar/baz".parse().unwrap(); + url_for_impl(&mut url, "/foo/:user/", { + let mut rv = HashMap::new(); + rv.insert("user".into(), "bam".into()); + rv + }); + assert_eq!(url.to_string(), "http://localhost/mounted/foo/bam/"); + } }