From 9f078abf191220774ae5e8e8bdd926bf89be848b Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Thu, 21 Jan 2021 15:54:13 +0000 Subject: [PATCH 1/3] Add JSON output for badges --- src/web/routes.rs | 3 +- src/web/rustdoc.rs | 140 ++++++++++++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/src/web/routes.rs b/src/web/routes.rs index 4f85190bb..da4625dda 100644 --- a/src/web/routes.rs +++ b/src/web/routes.rs @@ -115,7 +115,8 @@ pub(super) fn build_routes() -> Routes { routes.rustdoc_page("/:crate", super::rustdoc::rustdoc_redirector_handler); routes.rustdoc_page("/:crate/", super::rustdoc::rustdoc_redirector_handler); - routes.rustdoc_page("/:crate/badge.svg", super::rustdoc::badge_handler); + routes.rustdoc_page("/:crate/badge.svg", super::rustdoc::badge_handler_svg); + routes.rustdoc_page("/:crate/badge.json", super::rustdoc::badge_handler_json); routes.rustdoc_page( "/:crate/:version", super::rustdoc::rustdoc_redirector_handler, diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs index 195c1a158..bdb67b58d 100644 --- a/src/web/rustdoc.rs +++ b/src/web/rustdoc.rs @@ -11,7 +11,7 @@ use crate::{ }; use iron::url::percent_encoding::percent_decode; use iron::{ - headers::{CacheControl, CacheDirective, Expires, HttpDate}, + headers::{CacheControl, CacheDirective, ContentType, Expires, HttpDate}, modifiers::Redirect, status, Handler, IronResult, Request, Response, Url, }; @@ -206,7 +206,7 @@ impl RustdocPage { req: &mut Request, file_path: &str, ) -> IronResult { - use iron::{headers::ContentType, status::Status}; + use iron::status::Status; let templates = req .extensions @@ -557,9 +557,16 @@ pub fn target_redirect_handler(req: &mut Request) -> IronResult { Ok(resp) } -pub fn badge_handler(req: &mut Request) -> IronResult { - use badge::{Badge, BadgeOptions}; - use iron::headers::ContentType; +fn badge_handler_common( + req: &mut Request, + ext: &str, + content_type: ContentType, + builder: F, +) -> IronResult +where + F: Fn(String, String, String) -> Result, + E: std::fmt::Display, +{ let version = { let mut params = req.url.as_ref().query_pairs(); match params.find(|(key, _)| key == "version") { @@ -571,59 +578,51 @@ pub fn badge_handler(req: &mut Request) -> IronResult { let name = cexpect!(req, extension!(req, Router).find("crate")); let mut conn = extension!(req, Pool).get()?; - let options = - match match_version(&mut conn, &name, Some(&version)).and_then(|m| m.assume_exact()) { - Ok(MatchSemver::Exact((version, id))) => { - let rows = ctry!( - req, - conn.query( - "SELECT rustdoc_status + const SUCCESS_COLOR: &str = "#4d76ae"; + const FAILURE_COLOR: &str = "#e05d44"; + + let out = match match_version(&mut conn, &name, Some(&version)).and_then(|m| m.assume_exact()) { + Ok(MatchSemver::Exact((version, id))) => { + let rows = ctry!( + req, + conn.query( + "SELECT rustdoc_status FROM releases WHERE releases.id = $1", - &[&id] - ), - ); - if !rows.is_empty() && rows[0].get(0) { - BadgeOptions { - subject: "docs".to_owned(), - status: version, - color: "#4d76ae".to_owned(), - } - } else { - BadgeOptions { - subject: "docs".to_owned(), - status: version, - color: "#e05d44".to_owned(), - } - } + &[&id] + ), + ); + if !rows.is_empty() && rows[0].get(0) { + builder("docs".to_string(), version, SUCCESS_COLOR.to_string()) + } else { + builder("docs".to_string(), version, FAILURE_COLOR.to_string()) } + } - Ok(MatchSemver::Semver((version, _))) => { - let base_url = format!("{}/{}/badge.svg", redirect_base(req), name); - let url = ctry!( - req, - iron::url::Url::parse_with_params(&base_url, &[("version", version)]), - ); - let iron_url = ctry!(req, Url::from_generic_url(url)); - return Ok(super::redirect(iron_url)); - } + Ok(MatchSemver::Semver((version, _))) => { + let base_url = format!("{}/{}/badge.{}", redirect_base(req), name, ext); + let url = ctry!( + req, + iron::url::Url::parse_with_params(&base_url, &[("version", version)]), + ); + let iron_url = ctry!(req, Url::from_generic_url(url)); + return Ok(super::redirect(iron_url)); + } - Err(Nope::VersionNotFound) => BadgeOptions { - subject: "docs".to_owned(), - status: "version not found".to_owned(), - color: "#e05d44".to_owned(), - }, - - Err(_) => BadgeOptions { - subject: "docs".to_owned(), - status: "no builds".to_owned(), - color: "#e05d44".to_owned(), - }, - }; + Err(Nope::VersionNotFound) => builder( + "docs".to_string(), + "version not found".to_string(), + FAILURE_COLOR.to_string(), + ), + Err(_) => builder( + "docs".to_string(), + "no builds".to_string(), + FAILURE_COLOR.to_string(), + ), + }; - let mut resp = Response::with((status::Ok, ctry!(req, Badge::new(options)).to_svg())); - resp.headers - .set(ContentType("image/svg+xml".parse().unwrap())); + let mut resp = Response::with((status::Ok, ctry!(req, out))); + resp.headers.set(content_type); resp.headers.set(Expires(HttpDate(time::now()))); resp.headers.set(CacheControl(vec![ CacheDirective::NoCache, @@ -633,6 +632,43 @@ pub fn badge_handler(req: &mut Request) -> IronResult { Ok(resp) } +pub fn badge_handler_svg(req: &mut Request) -> IronResult { + use badge::{Badge, BadgeOptions}; + + let builder = |subject, status, color| { + let options = BadgeOptions { + subject, + status, + color, + }; + Badge::new(options).map(|badge| badge.to_svg()) + }; + badge_handler_common( + req, + "svg", + ContentType("image/svg+xml".parse().unwrap()), + builder, + ) +} + +pub fn badge_handler_json(req: &mut Request) -> IronResult { + use std::collections::HashMap; + let builder = |label, message, color| { + let mut out = HashMap::new(); + out.insert("schemaVersion", "1".to_string()); + out.insert("label", label); + out.insert("message", message); + out.insert("color", color); + serde_json::to_string(&out) + }; + badge_handler_common( + req, + "json", + ContentType("application/json".parse().unwrap()), + builder, + ) +} + /// Serves shared web resources used by rustdoc-generated documentation. /// /// This includes common `css` and `js` files that only change when the compiler is updated, but are From 87263f969cb526f83d31ae10c8dcd1459ce48d24 Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Thu, 21 Jan 2021 16:04:17 +0000 Subject: [PATCH 2/3] use a number for the schemaVersion --- src/web/rustdoc.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs index bdb67b58d..2ae22c0f2 100644 --- a/src/web/rustdoc.rs +++ b/src/web/rustdoc.rs @@ -652,13 +652,15 @@ pub fn badge_handler_svg(req: &mut Request) -> IronResult { } pub fn badge_handler_json(req: &mut Request) -> IronResult { + use serde_json::Value; use std::collections::HashMap; + let builder = |label, message, color| { let mut out = HashMap::new(); - out.insert("schemaVersion", "1".to_string()); - out.insert("label", label); - out.insert("message", message); - out.insert("color", color); + out.insert("schemaVersion", Value::Number(1.into())); + out.insert("label", Value::String(label)); + out.insert("message", Value::String(message)); + out.insert("color", Value::String(color)); serde_json::to_string(&out) }; badge_handler_common( From 1689137348857114626581a25ad8a2bad99e91d5 Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Thu, 21 Jan 2021 16:51:10 +0000 Subject: [PATCH 3/3] Add example to website --- templates/core/about/badges.html | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/templates/core/about/badges.html b/templates/core/about/badges.html index f2f6d24f0..2bebd5964 100644 --- a/templates/core/about/badges.html +++ b/templates/core/about/badges.html @@ -16,7 +16,7 @@

Badges

Badge will display in blue if docs.rs is successfully hosting your crate documentation, and red if building documentation failing. -

+

Example badges for mio crate:

@@ -48,6 +48,22 @@

Badges

-
mio
+ + +

+ If you want more customization, you can also get the badge data as JSON that + matches the the shields.io endpoint spec + by changing the extension from .svg to .json . +

+ +

+ {%- set rand_badge = "https://docs.rs/rand/badge.json" -%} + + For example, the image at + https://img.shields.io/endpoint?url={{ rand_badge }}&label=Documentation&color=yellow + outputs + rand +

+ {%- endblock body %}