diff --git a/src/engine/mod.rs b/src/engine/mod.rs index c3010c6..5166222 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -9,6 +9,7 @@ use hyper::Client; use hyper::client::HttpConnector; use hyper_tls::HttpsConnector; use relative_path::{RelativePath, RelativePathBuf}; +use semver::VersionReq; use slog::Logger; use tokio_service::Service; @@ -136,6 +137,23 @@ impl Engine { }) } + pub fn find_latest_crate_release(&self, name: CrateName, req: Option) -> + impl Future, Error=Error> + { + self.query_crate.call(name).from_err().map(move |query_response| { + if let Some(vreq) = req { + query_response.releases.iter() + .filter(|release| vreq.matches(&release.version)) + .max_by(|r1, r2| r1.version.cmp(&r2.version)) + .cloned() + } else { + query_response.releases.iter() + .max_by(|r1, r2| r1.version.cmp(&r2.version)) + .cloned() + } + }) + } + fn fetch_releases>(&self, names: I) -> impl Iterator, Error=Error>> { diff --git a/src/server/mod.rs b/src/server/mod.rs index f7b4752..f0900b2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,8 +1,9 @@ +use std::env; use std::sync::Arc; use futures::{Future, IntoFuture, future}; use hyper::{Error as HyperError, Method, Request, Response, StatusCode}; -use hyper::header::ContentType; +use hyper::header::{ContentType, Location}; use route_recognizer::{Params, Router}; use slog::Logger; use tokio_service::Service; @@ -11,7 +12,7 @@ mod assets; mod views; use ::engine::{Engine, AnalyzeDependenciesOutcome}; -use ::models::crates::CratePath; +use ::models::crates::{CrateName, CratePath}; use ::models::repo::RepoPath; use ::models::SubjectPath; @@ -31,6 +32,7 @@ enum Route { Index, Static(StaticFile), RepoStatus(StatusFormat), + CrateRedirect, CrateStatus(StatusFormat) } @@ -53,6 +55,7 @@ impl Server { router.add("/repo/:site/:qual/:name", Route::RepoStatus(StatusFormat::Html)); router.add("/repo/:site/:qual/:name/status.svg", Route::RepoStatus(StatusFormat::Svg)); + router.add("/crate/:name", Route::CrateRedirect); router.add("/crate/:name/:version", Route::CrateStatus(StatusFormat::Html)); router.add("/crate/:name/:version/status.svg", Route::CrateStatus(StatusFormat::Svg)); @@ -86,6 +89,11 @@ impl Service for Server { return Box::new(self.crate_status(req, route_match.params, logger, format)); } }, + &Route::CrateRedirect => { + if *req.method() == Method::Get { + return Box::new(self.crate_redirect(req, route_match.params, logger)); + } + }, &Route::Static(file) => { if *req.method() == Method::Get { return Box::new(future::ok(Server::static_file(file))); @@ -156,6 +164,55 @@ impl Server { }) } + fn crate_redirect(&self, _req: Request, params: Params, logger: Logger) -> + impl Future + { + let engine = self.engine.clone(); + + let name = params.find("name").expect("route param 'name' not found"); + + name.parse::().into_future().then(move |crate_name_result| { + match crate_name_result { + Err(err) => { + error!(logger, "error: {}", err); + let mut response = views::html::error::render("Could not parse crate name", + "Please make sure to provide a valid crate name."); + response.set_status(StatusCode::BadRequest); + future::Either::A(future::ok(response)) + }, + Ok(crate_name) => { + future::Either::B(engine.find_latest_crate_release(crate_name, None).then(move |release_result| { + match release_result { + Err(err) => { + error!(logger, "error: {}", err); + let mut response = views::html::error::render("Could not fetch crate information", + "Please make sure to provide a valid crate name."); + response.set_status(StatusCode::NotFound); + future::ok(response) + }, + Ok(None) => { + let mut response = views::html::error::render("Could not fetch crate information", + "Please make sure to provide a valid crate name."); + response.set_status(StatusCode::NotFound); + future::ok(response) + }, + Ok(Some(release)) => { + let mut response = Response::new(); + response.set_status(StatusCode::TemporaryRedirect); + let url = format!("{}/crate/{}/{}", + &SELF_BASE_URL as &str, + release.name.as_ref(), + release.version); + response.headers_mut().set(Location::new(url)); + future::ok(response) + } + } + })) + } + } + }) + } + fn crate_status(&self, _req: Request, params: Params, logger: Logger, format: StatusFormat) -> impl Future { @@ -216,3 +273,10 @@ impl Server { } } } + +lazy_static! { + static ref SELF_BASE_URL: String = { + env::var("BASE_URL") + .unwrap_or_else(|_| "http://localhost:8080".to_string()) + }; +} diff --git a/src/server/views/html/mod.rs b/src/server/views/html/mod.rs index 38e074d..b7337c0 100644 --- a/src/server/views/html/mod.rs +++ b/src/server/views/html/mod.rs @@ -9,12 +9,9 @@ pub mod index; pub mod error; pub mod status; -lazy_static! { - static ref SELF_BASE_URL: String = { - env::var("BASE_URL") - .unwrap_or_else(|_| "http://localhost:8080".to_string()) - }; +use super::super::SELF_BASE_URL; +lazy_static! { static ref GAUGES_SITE_ID: Option = { env::var("GAUGES_SITE_ID").ok().map(|s| s.to_string()) };