implement badges

This commit is contained in:
Sam Rijs 2018-01-27 12:40:17 +11:00
parent eb1bd1b698
commit adab7fdf74
6 changed files with 72 additions and 6 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="144" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="144" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h85v20H0z"/><path fill="#dfb317" d="M85 0h59v20H85z"/><path fill="url(#b)" d="M0 0h144v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="435" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="750">dependencies</text><text x="435" y="140" transform="scale(.1)" textLength="750">dependencies</text><text x="1135" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">outdated</text><text x="1135" y="140" transform="scale(.1)" textLength="490">outdated</text></g> </svg>

After

Width:  |  Height:  |  Size: 974 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="154" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="154" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h85v20H0z"/><path fill="#97CA00" d="M85 0h69v20H85z"/><path fill="url(#b)" d="M0 0h154v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="435" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="750">dependencies</text><text x="435" y="140" transform="scale(.1)" textLength="750">dependencies</text><text x="1185" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="590">up-to-date</text><text x="1185" y="140" transform="scale(.1)" textLength="590">up-to-date</text></g> </svg>

After

Width:  |  Height:  |  Size: 978 B

View file

@ -10,11 +10,13 @@ use serde_json;
use slog::Logger; use slog::Logger;
use tokio_service::Service; use tokio_service::Service;
use ::models::repo::RepoPath; use ::assets;
use ::engine::Engine; use ::engine::Engine;
use ::models::repo::RepoPath;
enum Route { enum Route {
AnalyzeDependencies StatusJson,
StatusSvg
} }
#[derive(Clone)] #[derive(Clone)]
@ -26,7 +28,8 @@ pub struct Api {
impl Api { impl Api {
pub fn new(engine: Engine) -> Api { pub fn new(engine: Engine) -> Api {
let mut router = Router::new(); let mut router = Router::new();
router.add("/repo/:site/:qual/:name/dependencies.json", Route::AnalyzeDependencies); router.add("/repo/:site/:qual/:name/status.json", Route::StatusJson);
router.add("/repo/:site/:qual/:name/status.svg", Route::StatusSvg);
Api { engine, router: Arc::new(router) } Api { engine, router: Arc::new(router) }
} }
@ -62,9 +65,14 @@ impl Service for Api {
fn call(&self, req: Request) -> Self::Future { fn call(&self, req: Request) -> Self::Future {
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 {
&Route::AnalyzeDependencies => { &Route::StatusJson => {
if *req.method() == Method::Get { if *req.method() == Method::Get {
return Box::new(self.analyze_dependencies(req, route_match.params)); return Box::new(self.status_json(req, route_match.params));
}
},
&Route::StatusSvg => {
if *req.method() == Method::Get {
return Box::new(self.status_svg(req, route_match.params));
} }
} }
} }
@ -77,7 +85,7 @@ impl Service for Api {
} }
impl Api { impl Api {
fn analyze_dependencies<'r>(&self, _req: Request, params: Params) -> impl Future<Item=Response, Error=HyperError> { fn status_json<'r>(&self, _req: Request, params: Params) -> impl Future<Item=Response, Error=HyperError> {
let engine = self.engine.clone(); let engine = self.engine.clone();
let site = params.find("site").expect("route param 'site' not found"); let site = params.find("site").expect("route param 'site' not found");
@ -136,4 +144,45 @@ impl Api {
} }
}) })
} }
fn status_svg<'r>(&self, _req: Request, params: Params) -> impl Future<Item=Response, Error=HyperError> {
let engine = self.engine.clone();
let site = params.find("site").expect("route param 'site' not found");
let qual = params.find("qual").expect("route param 'qual' 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| {
match repo_path_result {
Err(err) => {
let mut response = Response::new();
response.set_status(StatusCode::BadRequest);
response.set_body(format!("{:?}", err));
future::Either::A(future::ok(response))
},
Ok(repo_path) => {
future::Either::B(engine.analyze_dependencies(repo_path).then(|analyze_result| {
match analyze_result {
Err(err) => {
let mut response = Response::new();
response.set_status(StatusCode::InternalServerError);
response.set_body(format!("{:?}", err));
future::Either::A(future::ok(response))
},
Ok(analysis_outcome) => {
let mut response = Response::new()
.with_header(ContentType("image/svg+xml;charset=utf-8".parse().unwrap()));
if analysis_outcome.deps.any_outdated() {
response.set_body(assets::BADGE_OUTDATED_SVG.to_vec());
} else {
response.set_body(assets::BADGE_UPTODATE_SVG.to_vec());
}
future::Either::B(future::ok(response))
}
}
}))
}
}
})
}
} }

4
src/assets.rs Normal file
View file

@ -0,0 +1,4 @@
pub static BADGE_UPTODATE_SVG: &'static [u8; 978] =
include_bytes!("../assets/badges/up-to-date.svg");
pub static BADGE_OUTDATED_SVG: &'static [u8; 974] =
include_bytes!("../assets/badges/outdated.svg");

View file

@ -19,6 +19,7 @@ mod models;
mod parsers; mod parsers;
mod interactors; mod interactors;
mod engine; mod engine;
mod assets;
mod api; mod api;
use std::net::SocketAddr; use std::net::SocketAddr;

View file

@ -92,6 +92,16 @@ impl AnalyzedDependencies {
}).collect(); }).collect();
AnalyzedDependencies { main, dev, build } AnalyzedDependencies { main, dev, build }
} }
pub fn any_outdated(&self) -> bool {
let main_any_outdated = self.main.iter()
.any(|(_, dep)| dep.is_outdated());
let dev_any_outdated = self.dev.iter()
.any(|(_, dep)| dep.is_outdated());
let build_any_outdated = self.build.iter()
.any(|(_, dep)| dep.is_outdated());
main_any_outdated || dev_any_outdated || build_any_outdated
}
} }
#[derive(Debug)] #[derive(Debug)]