mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-25 11:36:30 +00:00
html frontend
This commit is contained in:
parent
21cd986d5e
commit
d821851fd8
12 changed files with 411 additions and 176 deletions
|
@ -7,6 +7,7 @@ authors = ["Sam Rijs <srijs@airpost.net>"]
|
|||
futures = "0.1.18"
|
||||
hyper = "0.11.15"
|
||||
hyper-tls = "0.1.2"
|
||||
maud = "0.17.2"
|
||||
route-recognizer = "0.1.12"
|
||||
semver = { version = "0.9.0", features = ["serde"] }
|
||||
serde = "1.0.27"
|
||||
|
|
70
assets/static/style.css
Normal file
70
assets/static/style.css
Normal file
|
@ -0,0 +1,70 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Fira+Sans:400,500,600,700');
|
||||
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro:400,600');
|
||||
|
||||
html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #eee;
|
||||
font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 30px 40px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
border-left: 5px solid #aaa;
|
||||
border-right: 5px solid #aaa;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace;
|
||||
white-space: pre-wrap;
|
||||
background: #dedede;
|
||||
color: #555;
|
||||
padding: .5em .3em;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace;
|
||||
background: #dedede;
|
||||
padding: .1em .3em;
|
||||
border-radius: .3em;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
h1 code {
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background: #f3f3f3;
|
||||
}
|
||||
|
||||
tr:nth-child(odd) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: .5em .7em;
|
||||
}
|
||||
|
||||
span.status {
|
||||
padding: .1em .3em;
|
||||
border-radius: .3em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span.status.up-to-date {
|
||||
background: #97ca00;
|
||||
}
|
||||
|
||||
span.status.outdated {
|
||||
background: #dfb317;
|
||||
}
|
167
src/api.rs
167
src/api.rs
|
@ -1,167 +0,0 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{Future, IntoFuture, future};
|
||||
use hyper::{Error as HyperError, Method, Request, Response, StatusCode};
|
||||
use hyper::header::ContentType;
|
||||
use route_recognizer::{Params, Router};
|
||||
use semver::{Version, VersionReq};
|
||||
use serde_json;
|
||||
use slog::Logger;
|
||||
use tokio_service::Service;
|
||||
|
||||
use ::assets;
|
||||
use ::engine::{Engine, AnalyzeDependenciesOutcome};
|
||||
use ::models::repo::RepoPath;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum StatusFormat {
|
||||
Json,
|
||||
Svg
|
||||
}
|
||||
|
||||
enum Route {
|
||||
Status(StatusFormat)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Api {
|
||||
engine: Engine,
|
||||
router: Arc<Router<Route>>
|
||||
}
|
||||
|
||||
impl Api {
|
||||
pub fn new(engine: Engine) -> Api {
|
||||
let mut router = Router::new();
|
||||
router.add("/repo/:site/:qual/:name/status.json", Route::Status(StatusFormat::Json));
|
||||
router.add("/repo/:site/:qual/:name/status.svg", Route::Status(StatusFormat::Svg));
|
||||
|
||||
Api { engine, router: Arc::new(router) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AnalyzeDependenciesResponseDetail {
|
||||
required: VersionReq,
|
||||
latest: Option<Version>,
|
||||
outdated: bool
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AnalyzeDependenciesResponseSingle {
|
||||
dependencies: BTreeMap<String, AnalyzeDependenciesResponseDetail>,
|
||||
#[serde(rename="dev-dependencies")]
|
||||
dev_dependencies: BTreeMap<String, AnalyzeDependenciesResponseDetail>,
|
||||
#[serde(rename="build-dependencies")]
|
||||
build_dependencies: BTreeMap<String, AnalyzeDependenciesResponseDetail>
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AnalyzeDependenciesResponse {
|
||||
crates: BTreeMap<String, AnalyzeDependenciesResponseSingle>
|
||||
}
|
||||
|
||||
impl Service for Api {
|
||||
type Request = Request;
|
||||
type Response = Response;
|
||||
type Error = HyperError;
|
||||
type Future = Box<Future<Item=Response, Error=HyperError>>;
|
||||
|
||||
fn call(&self, req: Request) -> Self::Future {
|
||||
if let Ok(route_match) = self.router.recognize(req.uri().path()) {
|
||||
match route_match.handler {
|
||||
&Route::Status(format) => {
|
||||
if *req.method() == Method::Get {
|
||||
return Box::new(self.status(req, route_match.params, format));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut response = Response::new();
|
||||
response.set_status(StatusCode::NotFound);
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
impl Api {
|
||||
fn status(&self, _req: Request, params: Params, format: StatusFormat) ->
|
||||
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(move |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 response = Api::status_format_analysis(analysis_outcome, format);
|
||||
future::Either::B(future::ok(response))
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn status_format_analysis(analysis_outcome: AnalyzeDependenciesOutcome, format: StatusFormat) -> Response {
|
||||
match format {
|
||||
StatusFormat::Json => {
|
||||
let single = AnalyzeDependenciesResponseSingle {
|
||||
dependencies: analysis_outcome.deps.main.into_iter()
|
||||
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
|
||||
outdated: analyzed.is_outdated(),
|
||||
required: analyzed.required,
|
||||
latest: analyzed.latest
|
||||
})).collect(),
|
||||
dev_dependencies: analysis_outcome.deps.dev.into_iter()
|
||||
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
|
||||
outdated: analyzed.is_outdated(),
|
||||
required: analyzed.required,
|
||||
latest: analyzed.latest
|
||||
})).collect(),
|
||||
build_dependencies: analysis_outcome.deps.build.into_iter()
|
||||
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
|
||||
outdated: analyzed.is_outdated(),
|
||||
required: analyzed.required,
|
||||
latest: analyzed.latest
|
||||
})).collect()
|
||||
};
|
||||
let multi = AnalyzeDependenciesResponse {
|
||||
crates: vec![(analysis_outcome.name.into(), single)].into_iter().collect()
|
||||
};
|
||||
Response::new()
|
||||
.with_header(ContentType::json())
|
||||
.with_body(serde_json::to_string(&multi).unwrap())
|
||||
},
|
||||
StatusFormat::Svg => {
|
||||
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());
|
||||
}
|
||||
response
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
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");
|
11
src/main.rs
11
src/main.rs
|
@ -1,9 +1,11 @@
|
|||
#![feature(ascii_ctype)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate hyper;
|
||||
extern crate hyper_tls;
|
||||
extern crate maud;
|
||||
extern crate route_recognizer;
|
||||
extern crate semver;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
@ -19,8 +21,7 @@ mod models;
|
|||
mod parsers;
|
||||
mod interactors;
|
||||
mod engine;
|
||||
mod assets;
|
||||
mod api;
|
||||
mod server;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Mutex;
|
||||
|
@ -32,7 +33,7 @@ use hyper_tls::HttpsConnector;
|
|||
use slog::Drain;
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
use self::api::Api;
|
||||
use self::server::Server;
|
||||
use self::engine::Engine;
|
||||
|
||||
fn main() {
|
||||
|
@ -63,9 +64,9 @@ fn main() {
|
|||
logger: logger.clone()
|
||||
};
|
||||
|
||||
let api = Api::new(engine);
|
||||
let server = Server::new(engine);
|
||||
|
||||
let serve = http.serve_addr_handle(&addr, &handle, move || Ok(api.clone()))
|
||||
let serve = http.serve_addr_handle(&addr, &handle, move || Ok(server.clone()))
|
||||
.expect("failed to bind server");
|
||||
|
||||
let serving = serve.for_each(move |conn| {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RepoPath {
|
||||
pub site: RepoSite,
|
||||
pub qual: RepoQualifier,
|
||||
|
@ -19,6 +20,7 @@ impl RepoPath {
|
|||
#[derive(Debug)]
|
||||
pub struct RepoValidationError;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RepoSite(String);
|
||||
|
||||
impl FromStr for RepoSite {
|
||||
|
@ -43,6 +45,7 @@ impl AsRef<str> for RepoSite {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RepoQualifier(String);
|
||||
|
||||
impl FromStr for RepoQualifier {
|
||||
|
@ -67,6 +70,7 @@ impl AsRef<str> for RepoQualifier {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RepoName(String);
|
||||
|
||||
impl FromStr for RepoName {
|
||||
|
|
9
src/server/assets.rs
Normal file
9
src/server/assets.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
//pub mod templates;
|
||||
|
||||
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");
|
||||
|
||||
pub static STATIC_STYLE_CSS: &'static str =
|
||||
include_str!("../../assets/static/style.css");
|
140
src/server/mod.rs
Normal file
140
src/server/mod.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use futures::{Future, IntoFuture, future};
|
||||
use hyper::{Error as HyperError, Method, Request, Response, StatusCode};
|
||||
use hyper::header::ContentType;
|
||||
use route_recognizer::{Params, Router};
|
||||
use slog::Logger;
|
||||
use tokio_service::Service;
|
||||
|
||||
mod assets;
|
||||
mod views;
|
||||
|
||||
use ::engine::{Engine, AnalyzeDependenciesOutcome};
|
||||
use ::models::repo::RepoPath;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum StatusFormat {
|
||||
Html,
|
||||
Json,
|
||||
Svg
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum StaticFile {
|
||||
StyleCss
|
||||
}
|
||||
|
||||
enum Route {
|
||||
Static(StaticFile),
|
||||
Status(StatusFormat)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Server {
|
||||
engine: Engine,
|
||||
router: Arc<Router<Route>>
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(engine: Engine) -> Server {
|
||||
let mut router = Router::new();
|
||||
|
||||
router.add("/static/style.css", Route::Static(StaticFile::StyleCss));
|
||||
|
||||
router.add("/repo/:site/:qual/:name", Route::Status(StatusFormat::Html));
|
||||
router.add("/repo/:site/:qual/:name/status.json", Route::Status(StatusFormat::Json));
|
||||
router.add("/repo/:site/:qual/:name/status.svg", Route::Status(StatusFormat::Svg));
|
||||
|
||||
Server { engine, router: Arc::new(router) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Server {
|
||||
type Request = Request;
|
||||
type Response = Response;
|
||||
type Error = HyperError;
|
||||
type Future = Box<Future<Item=Response, Error=HyperError>>;
|
||||
|
||||
fn call(&self, req: Request) -> Self::Future {
|
||||
if let Ok(route_match) = self.router.recognize(req.uri().path()) {
|
||||
match route_match.handler {
|
||||
&Route::Status(format) => {
|
||||
if *req.method() == Method::Get {
|
||||
return Box::new(self.status(req, route_match.params, format));
|
||||
}
|
||||
},
|
||||
&Route::Static(file) => {
|
||||
if *req.method() == Method::Get {
|
||||
return Box::new(future::ok(Server::static_file(file)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let mut response = Response::new();
|
||||
response.set_status(StatusCode::NotFound);
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
impl Server {
|
||||
fn status(&self, _req: Request, params: Params, format: StatusFormat) ->
|
||||
impl Future<Item=Response, Error=HyperError>
|
||||
{
|
||||
let server = self.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(server.engine.analyze_dependencies(repo_path.clone()).then(move |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 response = Server::status_format_analysis(analysis_outcome, format, repo_path);
|
||||
future::Either::B(future::ok(response))
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn status_format_analysis(analysis_outcome: AnalyzeDependenciesOutcome, format: StatusFormat, repo_path: RepoPath) -> Response {
|
||||
match format {
|
||||
StatusFormat::Json =>
|
||||
views::status_json(analysis_outcome),
|
||||
StatusFormat::Svg =>
|
||||
views::status_svg(analysis_outcome),
|
||||
StatusFormat::Html =>
|
||||
views::status_html(analysis_outcome, repo_path)
|
||||
}
|
||||
}
|
||||
|
||||
fn static_file(file: StaticFile) -> Response {
|
||||
match file {
|
||||
StaticFile::StyleCss => {
|
||||
Response::new()
|
||||
.with_header(ContentType("text/css".parse().unwrap()))
|
||||
.with_body(assets::STATIC_STYLE_CSS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/server/views/mod.rs
Normal file
8
src/server/views/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
mod status_html;
|
||||
pub use self::status_html::status_html;
|
||||
|
||||
mod status_json;
|
||||
pub use self::status_json::status_json;
|
||||
|
||||
mod status_svg;
|
||||
pub use self::status_svg::status_svg;
|
97
src/server/views/status_html.rs
Normal file
97
src/server/views/status_html.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use hyper::Response;
|
||||
use hyper::header::ContentType;
|
||||
use maud::{Markup, html};
|
||||
|
||||
use ::engine::AnalyzeDependenciesOutcome;
|
||||
use ::models::crates::{CrateName, AnalyzedDependency};
|
||||
use ::models::repo::RepoPath;
|
||||
|
||||
const SELF_BASE_URL: &'static str = "http://example.com";
|
||||
|
||||
fn dependency_table<I: IntoIterator<Item=(CrateName, AnalyzedDependency)>>(deps: I) -> Markup {
|
||||
html! {
|
||||
table {
|
||||
tr {
|
||||
th "Crate"
|
||||
th "Required"
|
||||
th "Latest"
|
||||
th "Status"
|
||||
}
|
||||
@for (name, dep) in deps {
|
||||
tr {
|
||||
td {
|
||||
a href=(format!("https://crates.io/crates/{}", name.as_ref())) (name.as_ref())
|
||||
}
|
||||
td code (dep.required.to_string())
|
||||
td {
|
||||
@if let Some(ref latest) = dep.latest {
|
||||
code (latest.to_string())
|
||||
} @else {
|
||||
"N/A"
|
||||
}
|
||||
}
|
||||
td {
|
||||
@if dep.is_outdated() {
|
||||
span class="status outdated" "out of date"
|
||||
} @else {
|
||||
span class="status up-to-date" "up to date"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_html(analysis_outcome: AnalyzeDependenciesOutcome, repo_path: RepoPath) -> Response {
|
||||
let self_path = format!("repo/{}/{}/{}", repo_path.site.as_ref(), repo_path.qual.as_ref(), repo_path.name.as_ref());
|
||||
let status_base_url = format!("{}/{}", SELF_BASE_URL, self_path);
|
||||
let title = format!("{} / {} - Dependency Status", repo_path.qual.as_ref(), repo_path.name.as_ref());
|
||||
|
||||
let rendered = html! {
|
||||
html {
|
||||
head {
|
||||
title (title)
|
||||
link rel="stylesheet" type="text/css" href="/static/style.css";
|
||||
}
|
||||
body {
|
||||
header {
|
||||
h1 {
|
||||
"Dependency status for "
|
||||
code (format!("{}/{}", repo_path.qual.as_ref(), repo_path.name.as_ref()))
|
||||
}
|
||||
|
||||
h2 {
|
||||
"Crate "
|
||||
code (analysis_outcome.name.as_ref())
|
||||
}
|
||||
|
||||
img src=(format!("/{}/status.svg", self_path));
|
||||
|
||||
pre {
|
||||
(format!("[![dependency status]({}/status.svg)]({})", status_base_url, status_base_url))
|
||||
}
|
||||
|
||||
@if !analysis_outcome.deps.main.is_empty() {
|
||||
h3 "Dependencies"
|
||||
(dependency_table(analysis_outcome.deps.main))
|
||||
}
|
||||
|
||||
@if !analysis_outcome.deps.dev.is_empty() {
|
||||
h3 "Dev dependencies"
|
||||
(dependency_table(analysis_outcome.deps.dev))
|
||||
}
|
||||
|
||||
@if !analysis_outcome.deps.build.is_empty() {
|
||||
h3 "Build dependencies"
|
||||
(dependency_table(analysis_outcome.deps.build))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Response::new()
|
||||
.with_header(ContentType::html())
|
||||
.with_body(rendered.0)
|
||||
}
|
60
src/server/views/status_json.rs
Normal file
60
src/server/views/status_json.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use hyper::Response;
|
||||
use hyper::header::ContentType;
|
||||
use semver::{Version, VersionReq};
|
||||
use serde_json;
|
||||
|
||||
use ::engine::AnalyzeDependenciesOutcome;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AnalyzeDependenciesResponseDetail {
|
||||
required: VersionReq,
|
||||
latest: Option<Version>,
|
||||
outdated: bool
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AnalyzeDependenciesResponseSingle {
|
||||
dependencies: BTreeMap<String, AnalyzeDependenciesResponseDetail>,
|
||||
#[serde(rename="dev-dependencies")]
|
||||
dev_dependencies: BTreeMap<String, AnalyzeDependenciesResponseDetail>,
|
||||
#[serde(rename="build-dependencies")]
|
||||
build_dependencies: BTreeMap<String, AnalyzeDependenciesResponseDetail>
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AnalyzeDependenciesResponse {
|
||||
crates: BTreeMap<String, AnalyzeDependenciesResponseSingle>
|
||||
}
|
||||
|
||||
pub fn status_json(analysis_outcome: AnalyzeDependenciesOutcome) -> Response {
|
||||
let single = AnalyzeDependenciesResponseSingle {
|
||||
dependencies: analysis_outcome.deps.main.into_iter()
|
||||
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
|
||||
outdated: analyzed.is_outdated(),
|
||||
required: analyzed.required,
|
||||
latest: analyzed.latest
|
||||
})).collect(),
|
||||
dev_dependencies: analysis_outcome.deps.dev.into_iter()
|
||||
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
|
||||
outdated: analyzed.is_outdated(),
|
||||
required: analyzed.required,
|
||||
latest: analyzed.latest
|
||||
})).collect(),
|
||||
build_dependencies: analysis_outcome.deps.build.into_iter()
|
||||
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
|
||||
outdated: analyzed.is_outdated(),
|
||||
required: analyzed.required,
|
||||
latest: analyzed.latest
|
||||
})).collect()
|
||||
};
|
||||
|
||||
let multi = AnalyzeDependenciesResponse {
|
||||
crates: vec![(analysis_outcome.name.into(), single)].into_iter().collect()
|
||||
};
|
||||
|
||||
Response::new()
|
||||
.with_header(ContentType::json())
|
||||
.with_body(serde_json::to_string(&multi).unwrap())
|
||||
}
|
16
src/server/views/status_svg.rs
Normal file
16
src/server/views/status_svg.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use hyper::Response;
|
||||
use hyper::header::ContentType;
|
||||
|
||||
use ::server::assets;
|
||||
use ::engine::AnalyzeDependenciesOutcome;
|
||||
|
||||
pub fn status_svg(analysis_outcome: AnalyzeDependenciesOutcome) -> Response {
|
||||
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());
|
||||
}
|
||||
response
|
||||
}
|
Loading…
Reference in a new issue