diff --git a/Cargo.toml b/Cargo.toml index 784280f..8808596 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Sam Rijs "] [dependencies] +base64 = "0.9.0" futures = "0.1.18" hyper = "0.11.15" hyper-tls = "0.1.2" diff --git a/assets/static/style.css b/assets/static/style.css index f19bd43..31489e2 100644 --- a/assets/static/style.css +++ b/assets/static/style.css @@ -1,82 +1 @@ -@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: #f3f3f3; - font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -} - -body { - padding: 30px 40px; - margin: 0 auto; - background: #fff; - border-left: 5px solid #eee; - border-right: 5px solid #eee; - width: 800px; -} - -pre { - font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace; - overflow: auto; - word-wrap: normal; - white-space: pre; - background: #dedede; - color: #555; - padding: .5em .7em; -} - -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 a { - text-decoration: none; -} - -h1 code { - letter-spacing: -0.02em; -} - -h3 span.summary { - font-weight: normal; - font-style: italic; - font-size: .9em; -} - -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; -} +@import url('https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css') diff --git a/src/main.rs b/src/main.rs index 8a8994e..6771302 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ #![feature(ip_constructors)] #![feature(proc_macro)] +extern crate base64; extern crate futures; extern crate hyper; extern crate hyper_tls; diff --git a/src/server/views/status_html.rs b/src/server/views/status_html.rs index 5964857..289b34f 100644 --- a/src/server/views/status_html.rs +++ b/src/server/views/status_html.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; +use base64::display::Base64Display; use hyper::Response; use hyper::header::ContentType; use maud::{Markup, html}; @@ -7,6 +8,7 @@ use maud::{Markup, html}; use ::engine::AnalyzeDependenciesOutcome; use ::models::crates::{CrateName, AnalyzedDependency}; use ::models::repo::RepoPath; +use ::server::assets; const SELF_BASE_URL: &'static str = "https://shiny-robots.herokuapp.com"; @@ -15,42 +17,44 @@ fn dependency_table(title: &str, deps: BTreeMap) let count_outdated = deps.iter().filter(|&(_, dep)| dep.is_outdated()).count(); html! { - h3 { - (title) - span class="summary" { - @if count_outdated > 0 { - (format!(" ({} total, {} up-to-date, {} outdated)", count_total, count_total - count_outdated, count_outdated)) - } @else { - (format!(" ({} total, all up-to-date)", count_total)) - } + h3 class="title is-4" (title) + p class="subtitle is-4" { + @if count_outdated > 0 { + (format!(" ({} total, {} up-to-date, {} outdated)", count_total, count_total - count_outdated, count_outdated)) + } @else { + (format!(" ({} total, all up-to-date)", count_total)) } } - table { - tr { - th "Crate" - th "Required" - th "Latest" - th "Status" - } - @for (name, dep) in deps { + table class="table is-fullwidth is-striped is-hoverable" { + thead { 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" + th "Crate" + th "Required" + th "Latest" + th "Status" + } + } + tbody { + @for (name, dep) in deps { + tr { + td { + a href=(format!("https://crates.io/crates/{}", name.as_ref())) (name.as_ref()) } - } - td { - @if dep.is_outdated() { - span class="status outdated" "out of date" - } @else { - span class="status up-to-date" "up to date" + 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="tag is-warning" "out of date" + } @else { + span class="tag is-success" "up to date" + } } } } @@ -64,6 +68,14 @@ pub fn status_html(analysis_outcome: AnalyzeDependenciesOutcome, repo_path: Repo 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 (hero_class, status_asset) = if analysis_outcome.deps.any_outdated() { + ("is-warning", assets::BADGE_OUTDATED_SVG.as_ref()) + } else { + ("is-success", assets::BADGE_UPTODATE_SVG.as_ref()) + }; + + let status_data_url = format!("data:image/svg+xml;base64,{}", Base64Display::standard(status_asset)); + let rendered = html! { html { head { @@ -71,35 +83,45 @@ pub fn status_html(analysis_outcome: AnalyzeDependenciesOutcome, repo_path: Repo link rel="stylesheet" type="text/css" href="/static/style.css"; } body { - header { - h1 { - "Dependency status for " - a href=(format!("{}/{}/{}", repo_path.site.to_base_uri(), repo_path.qual.as_ref(), repo_path.name.as_ref())) { - code (format!("{}/{}", repo_path.qual.as_ref(), repo_path.name.as_ref())) + section class=(format!("hero {}", hero_class)) { + div class="hero-body" { + div class="container" { + h1 class="title is-2" { + "Dependency status for " + a href=(format!("{}/{}/{}", repo_path.site.to_base_uri(), repo_path.qual.as_ref(), repo_path.name.as_ref())) { + code (format!("{}/{}", repo_path.qual.as_ref(), repo_path.name.as_ref())) + } + } + + img src=(status_data_url); } } - - img src=(format!("/{}/status.svg", self_path)); - - pre { - (format!("[![dependency status]({}/status.svg)]({})", status_base_url, status_base_url)) + div class="hero-footer" { + div class="container" { + pre class="is-size-7" { + (format!("[![dependency status]({}/status.svg)]({})", status_base_url, status_base_url)) + } + } } + } + section class="section" { + div class="container" { + h2 class="title is-3" { + "Crate " + code (analysis_outcome.name.as_ref()) + } - h2 { - "Crate " - code (analysis_outcome.name.as_ref()) - } + @if !analysis_outcome.deps.main.is_empty() { + (dependency_table("Dependencies", analysis_outcome.deps.main)) + } - @if !analysis_outcome.deps.main.is_empty() { - (dependency_table("Dependencies", analysis_outcome.deps.main)) - } + @if !analysis_outcome.deps.dev.is_empty() { + (dependency_table("Dev dependencies", analysis_outcome.deps.dev)) + } - @if !analysis_outcome.deps.dev.is_empty() { - (dependency_table("Dev dependencies", analysis_outcome.deps.dev)) - } - - @if !analysis_outcome.deps.build.is_empty() { - (dependency_table("Build dependencies", analysis_outcome.deps.build)) + @if !analysis_outcome.deps.build.is_empty() { + (dependency_table("Build dependencies", analysis_outcome.deps.build)) + } } } }