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
 |
-
+
+
+
+ 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
+
+
+
{%- endblock body %}