replace lazy-static with once-cell

This commit is contained in:
Rob Ede 2020-09-28 23:50:12 +01:00
parent 1d5fdd5dc5
commit 63a8355543
No known key found for this signature in database
GPG key ID: C2A3B36E841A91E6
5 changed files with 190 additions and 131 deletions

1
Cargo.lock generated
View file

@ -1249,7 +1249,6 @@ dependencies = [
"hyper 0.11.27", "hyper 0.11.27",
"hyper-tls", "hyper-tls",
"indexmap", "indexmap",
"lazy_static 1.4.0",
"lru-cache", "lru-cache",
"maud", "maud",
"once_cell", "once_cell",

View file

@ -19,9 +19,9 @@ futures = "0.1.18"
hyper = "0.11.15" hyper = "0.11.15"
hyper-tls = "0.1.2" hyper-tls = "0.1.2"
indexmap = { version = "1.0.0", features = ["serde-1"] } indexmap = { version = "1.0.0", features = ["serde-1"] }
lazy_static = "1.0.0"
lru-cache = "0.1.1" lru-cache = "0.1.1"
maud = "0.22" maud = "0.22"
once_cell = "1.4"
relative-path = { version = "0.3.7", features = ["serde"] } relative-path = { version = "0.3.7", features = ["serde"] }
route-recognizer = "0.1.12" route-recognizer = "0.1.12"
rustsec = "0.6.0" rustsec = "0.6.0"
@ -36,7 +36,6 @@ tokio-core = "0.1.12"
tokio-service = "0.1.0" tokio-service = "0.1.0"
toml = "0.4.5" toml = "0.4.5"
try_future = "0.1.1" try_future = "0.1.1"
once_cell = "1.4"
[build-dependencies] [build-dependencies]

View file

@ -1,11 +1,8 @@
#![deny(bare_trait_objects)] #![deny(rust_2018_idioms)]
#![allow(unused)]
#[macro_use] #[macro_use]
extern crate failure; extern crate failure;
#[macro_use] #[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use] #[macro_use]
extern crate slog; extern crate slog;

View file

@ -1,9 +1,10 @@
use std::env; use std::env;
use std::sync::Arc; use std::sync::Arc;
use futures::{Future, IntoFuture, future}; use futures::{future, Future, IntoFuture};
use hyper::{Error as HyperError, Method, Request, Response, StatusCode};
use hyper::header::{ContentType, Location}; use hyper::header::{ContentType, Location};
use hyper::{Error as HyperError, Method, Request, Response, StatusCode};
use once_cell::sync::Lazy;
use route_recognizer::{Params, Router}; use route_recognizer::{Params, Router};
use semver::VersionReq; use semver::VersionReq;
use slog::Logger; use slog::Logger;
@ -12,7 +13,7 @@ use tokio_service::Service;
mod assets; mod assets;
mod views; mod views;
use crate::engine::{Engine, AnalyzeDependenciesOutcome}; use crate::engine::{AnalyzeDependenciesOutcome, Engine};
use crate::models::crates::{CrateName, CratePath}; use crate::models::crates::{CrateName, CratePath};
use crate::models::repo::RepoPath; use crate::models::repo::RepoPath;
use crate::models::SubjectPath; use crate::models::SubjectPath;
@ -20,13 +21,13 @@ use crate::models::SubjectPath;
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
enum StatusFormat { enum StatusFormat {
Html, Html,
Svg Svg,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum StaticFile { enum StaticFile {
StyleCss, StyleCss,
FaviconPng FaviconPng,
} }
enum Route { enum Route {
@ -34,14 +35,14 @@ enum Route {
Static(StaticFile), Static(StaticFile),
RepoStatus(StatusFormat), RepoStatus(StatusFormat),
CrateRedirect, CrateRedirect,
CrateStatus(StatusFormat) CrateStatus(StatusFormat),
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Server { pub struct Server {
logger: Logger, logger: Logger,
engine: Engine, engine: Engine,
router: Arc<Router<Route>> router: Arc<Router<Route>>,
} }
impl Server { impl Server {
@ -53,14 +54,30 @@ impl Server {
router.add("/static/style.css", Route::Static(StaticFile::StyleCss)); router.add("/static/style.css", Route::Static(StaticFile::StyleCss));
router.add("/static/favicon.png", Route::Static(StaticFile::FaviconPng)); router.add("/static/favicon.png", Route::Static(StaticFile::FaviconPng));
router.add("/repo/:site/:qual/:name", Route::RepoStatus(StatusFormat::Html)); router.add(
router.add("/repo/:site/:qual/:name/status.svg", Route::RepoStatus(StatusFormat::Svg)); "/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", Route::CrateRedirect);
router.add("/crate/:name/:version", Route::CrateStatus(StatusFormat::Html)); router.add(
router.add("/crate/:name/:version/status.svg", Route::CrateStatus(StatusFormat::Svg)); "/crate/:name/:version",
Route::CrateStatus(StatusFormat::Html),
);
router.add(
"/crate/:name/:version/status.svg",
Route::CrateStatus(StatusFormat::Svg),
);
Server { logger, engine, router: Arc::new(router) } Server {
logger,
engine,
router: Arc::new(router),
}
} }
} }
@ -68,10 +85,12 @@ impl Service for Server {
type Request = Request; type Request = Request;
type Response = Response; type Response = Response;
type Error = HyperError; type Error = HyperError;
type Future = Box<dyn Future<Item=Response, Error=HyperError>>; type Future = Box<dyn Future<Item = Response, Error = HyperError>>;
fn call(&self, req: Request) -> Self::Future { fn call(&self, req: Request) -> Self::Future {
let logger = self.logger.new(o!("http_path" => req.uri().path().to_owned())); let logger = self
.logger
.new(o!("http_path" => req.uri().path().to_owned()));
if let Ok(route_match) = self.router.recognize(req.uri().path()) { if let Ok(route_match) = self.router.recognize(req.uri().path()) {
match route_match.handler { match route_match.handler {
@ -79,28 +98,32 @@ impl Service for Server {
if *req.method() == Method::Get { if *req.method() == Method::Get {
return Box::new(self.index(req, route_match.params, logger)); return Box::new(self.index(req, route_match.params, logger));
} }
}, }
&Route::RepoStatus(format) => { &Route::RepoStatus(format) => {
if *req.method() == Method::Get { if *req.method() == Method::Get {
return Box::new(self.repo_status(req, route_match.params, logger, format)); return Box::new(self.repo_status(req, route_match.params, logger, format));
} }
}, }
&Route::CrateStatus(format) => { &Route::CrateStatus(format) => {
if *req.method() == Method::Get { if *req.method() == Method::Get {
return Box::new(self.crate_status(req, route_match.params, logger, format)); return Box::new(self.crate_status(
req,
route_match.params,
logger,
format,
));
} }
}, }
&Route::CrateRedirect => { &Route::CrateRedirect => {
if *req.method() == Method::Get { if *req.method() == Method::Get {
return Box::new(self.crate_redirect(req, route_match.params, logger)); return Box::new(self.crate_redirect(req, route_match.params, logger));
} }
}, }
&Route::Static(file) => { &Route::Static(file) => {
if *req.method() == Method::Get { if *req.method() == Method::Get {
return Box::new(future::ok(Server::static_file(file))); return Box::new(future::ok(Server::static_file(file)));
} }
} }
} }
} }
@ -111,175 +134,216 @@ impl Service for Server {
} }
impl Server { impl Server {
fn index(&self, _req: Request, _params: Params, logger: Logger) -> fn index(
impl Future<Item=Response, Error=HyperError> &self,
{ _req: Request,
self.engine.get_popular_repos() _params: Params,
logger: Logger,
) -> impl Future<Item = Response, Error = HyperError> {
self.engine
.get_popular_repos()
.join(self.engine.get_popular_crates()) .join(self.engine.get_popular_crates())
.then(move |popular_result| { .then(move |popular_result| match popular_result {
match popular_result { Err(err) => {
Err(err) => { error!(logger, "error: {}", err);
error!(logger, "error: {}", err); let mut response =
let mut response = views::html::error::render("Could not retrieve popular items", ""); views::html::error::render("Could not retrieve popular items", "");
response.set_status(StatusCode::InternalServerError); response.set_status(StatusCode::InternalServerError);
future::ok(response) future::ok(response)
}, }
Ok((popular_repos, popular_crates)) => Ok((popular_repos, popular_crates)) => {
future::ok(views::html::index::render(popular_repos, popular_crates)) future::ok(views::html::index::render(popular_repos, popular_crates))
} }
}) })
} }
fn repo_status(&self, _req: Request, params: Params, logger: Logger, format: StatusFormat) -> fn repo_status(
impl Future<Item=Response, Error=HyperError> &self,
{ _req: Request,
params: Params,
logger: Logger,
format: StatusFormat,
) -> impl Future<Item = Response, Error = HyperError> {
let server = self.clone(); let server = self.clone();
let site = params.find("site").expect("route param 'site' not found"); let site = params.find("site").expect("route param 'site' not found");
let qual = params.find("qual").expect("route param 'qual' not found"); let qual = params.find("qual").expect("route param 'qual' not found");
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
RepoPath::from_parts(site, qual, name).into_future().then(move |repo_path_result| { RepoPath::from_parts(site, qual, name)
match repo_path_result { .into_future()
.then(move |repo_path_result| match repo_path_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let mut response = views::html::error::render("Could not parse repository path", let mut response = views::html::error::render(
"Please make sure to provide a valid repository path."); "Could not parse repository path",
"Please make sure to provide a valid repository path.",
);
response.set_status(StatusCode::BadRequest); response.set_status(StatusCode::BadRequest);
future::Either::A(future::ok(response)) future::Either::A(future::ok(response))
}, }
Ok(repo_path) => { Ok(repo_path) => future::Either::B(
future::Either::B(server.engine.analyze_repo_dependencies(repo_path.clone()).then(move |analyze_result| { server
match analyze_result { .engine
.analyze_repo_dependencies(repo_path.clone())
.then(move |analyze_result| match analyze_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let response = Server::status_format_analysis(None, format, SubjectPath::Repo(repo_path)); let response = Server::status_format_analysis(
future::ok(response) None,
}, format,
Ok(analysis_outcome) => { SubjectPath::Repo(repo_path),
let response = Server::status_format_analysis(Some(analysis_outcome), format, SubjectPath::Repo(repo_path)); );
future::ok(response) future::ok(response)
} }
} Ok(analysis_outcome) => {
})) let response = Server::status_format_analysis(
} Some(analysis_outcome),
} format,
}) SubjectPath::Repo(repo_path),
);
future::ok(response)
}
}),
),
})
} }
fn crate_redirect(&self, _req: Request, params: Params, logger: Logger) -> fn crate_redirect(
impl Future<Item=Response, Error=HyperError> &self,
{ _req: Request,
params: Params,
logger: Logger,
) -> impl Future<Item = Response, Error = HyperError> {
let engine = self.engine.clone(); let engine = self.engine.clone();
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
name.parse::<CrateName>().into_future().then(move |crate_name_result| { name.parse::<CrateName>()
match crate_name_result { .into_future()
.then(move |crate_name_result| match crate_name_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let mut response = views::html::error::render("Could not parse crate name", let mut response = views::html::error::render(
"Please make sure to provide a valid crate name."); "Could not parse crate name",
"Please make sure to provide a valid crate name.",
);
response.set_status(StatusCode::BadRequest); response.set_status(StatusCode::BadRequest);
future::Either::A(future::ok(response)) future::Either::A(future::ok(response))
}, }
Ok(crate_name) => { Ok(crate_name) => future::Either::B(
future::Either::B(engine.find_latest_crate_release(crate_name, VersionReq::any()).then(move |release_result| { engine
match release_result { .find_latest_crate_release(crate_name, VersionReq::any())
.then(move |release_result| match release_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let mut response = views::html::error::render("Could not fetch crate information", let mut response = views::html::error::render(
"Please make sure to provide a valid crate name."); "Could not fetch crate information",
"Please make sure to provide a valid crate name.",
);
response.set_status(StatusCode::NotFound); response.set_status(StatusCode::NotFound);
future::ok(response) future::ok(response)
}, }
Ok(None) => { Ok(None) => {
let mut response = views::html::error::render("Could not fetch crate information", let mut response = views::html::error::render(
"Please make sure to provide a valid crate name."); "Could not fetch crate information",
"Please make sure to provide a valid crate name.",
);
response.set_status(StatusCode::NotFound); response.set_status(StatusCode::NotFound);
future::ok(response) future::ok(response)
}, }
Ok(Some(release)) => { Ok(Some(release)) => {
let mut response = Response::new(); let mut response = Response::new();
response.set_status(StatusCode::TemporaryRedirect); response.set_status(StatusCode::TemporaryRedirect);
let url = format!("{}/crate/{}/{}", let url = format!(
"{}/crate/{}/{}",
&SELF_BASE_URL as &str, &SELF_BASE_URL as &str,
release.name.as_ref(), release.name.as_ref(),
release.version); release.version
);
response.headers_mut().set(Location::new(url)); response.headers_mut().set(Location::new(url));
future::ok(response) future::ok(response)
} }
} }),
})) ),
} })
}
})
} }
fn crate_status(&self, _req: Request, params: Params, logger: Logger, format: StatusFormat) -> fn crate_status(
impl Future<Item=Response, Error=HyperError> &self,
{ _req: Request,
params: Params,
logger: Logger,
format: StatusFormat,
) -> impl Future<Item = Response, Error = HyperError> {
let server = self.clone(); let server = self.clone();
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
let version = params.find("version").expect("route param 'version' not found"); let version = params
.find("version")
.expect("route param 'version' not found");
CratePath::from_parts(name, version).into_future().then(move |crate_path_result| { CratePath::from_parts(name, version)
match crate_path_result { .into_future()
.then(move |crate_path_result| match crate_path_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let mut response = views::html::error::render("Could not parse crate path", let mut response = views::html::error::render(
"Please make sure to provide a valid crate name and version."); "Could not parse crate path",
"Please make sure to provide a valid crate name and version.",
);
response.set_status(StatusCode::BadRequest); response.set_status(StatusCode::BadRequest);
future::Either::A(future::ok(response)) future::Either::A(future::ok(response))
}, }
Ok(crate_path) => { Ok(crate_path) => future::Either::B(
future::Either::B(server.engine.analyze_crate_dependencies(crate_path.clone()).then(move |analyze_result| { server
match analyze_result { .engine
.analyze_crate_dependencies(crate_path.clone())
.then(move |analyze_result| match analyze_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let response = Server::status_format_analysis(None, format, SubjectPath::Crate(crate_path)); let response = Server::status_format_analysis(
future::ok(response) None,
}, format,
Ok(analysis_outcome) => { SubjectPath::Crate(crate_path),
let response = Server::status_format_analysis(Some(analysis_outcome), format, SubjectPath::Crate(crate_path)); );
future::ok(response) future::ok(response)
} }
} Ok(analysis_outcome) => {
})) let response = Server::status_format_analysis(
} Some(analysis_outcome),
} format,
}) SubjectPath::Crate(crate_path),
);
future::ok(response)
}
}),
),
})
} }
fn status_format_analysis(analysis_outcome: Option<AnalyzeDependenciesOutcome>, format: StatusFormat, subject_path: SubjectPath) -> Response { fn status_format_analysis(
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
format: StatusFormat,
subject_path: SubjectPath,
) -> Response {
match format { match format {
StatusFormat::Svg => StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref()),
views::badge::response(analysis_outcome.as_ref()), StatusFormat::Html => views::html::status::render(analysis_outcome, subject_path),
StatusFormat::Html =>
views::html::status::render(analysis_outcome, subject_path)
} }
} }
fn static_file(file: StaticFile) -> Response { fn static_file(file: StaticFile) -> Response {
match file { match file {
StaticFile::StyleCss => { StaticFile::StyleCss => Response::new()
Response::new() .with_header(ContentType("text/css".parse().unwrap()))
.with_header(ContentType("text/css".parse().unwrap())) .with_body(assets::STATIC_STYLE_CSS),
.with_body(assets::STATIC_STYLE_CSS) StaticFile::FaviconPng => Response::new()
}, .with_header(ContentType("image/png".parse().unwrap()))
StaticFile::FaviconPng => { .with_body(assets::STATIC_FAVICON_PNG.to_vec()),
Response::new()
.with_header(ContentType("image/png".parse().unwrap()))
.with_body(assets::STATIC_FAVICON_PNG.to_vec())
}
} }
} }
} }
lazy_static! { static SELF_BASE_URL: Lazy<String> =
static ref SELF_BASE_URL: String = { Lazy::new(|| env::var("BASE_URL").unwrap_or_else(|_| "http://localhost:8080".to_string()));
env::var("BASE_URL")
.unwrap_or_else(|_| "http://localhost:8080".to_string())
};
}

View file

@ -24,7 +24,7 @@ impl<S> Debug for Cache<S>
where S: Service<Error=Error> + Debug, where S: Service<Error=Error> + Debug,
S::Request: Hash + Eq S::Request: Hash + Eq
{ {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult { fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
fmt.debug_struct("Cache") fmt.debug_struct("Cache")
.field("inner", &self.inner) .field("inner", &self.inner)
.field("duration", &self.duration) .field("duration", &self.duration)
@ -76,7 +76,7 @@ impl<F> Debug for Cached<F>
where F: Future<Error=Error> + Debug, where F: Future<Error=Error> + Debug,
F::Item: Debug F::Item: Debug
{ {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult { fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
self.0.fmt(fmt) self.0.fmt(fmt)
} }
} }