Skip to content

Commit 99f0298

Browse files
committed
Generate maintenance badges via badge crate
1 parent 9148682 commit 99f0298

File tree

6 files changed

+115
-61
lines changed

6 files changed

+115
-61
lines changed

Cargo.lock

+43
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ rustdoc-args = [
3030
[dependencies]
3131
ammonia = "3.0.0"
3232
anyhow = "1.0"
33+
badge = "0.3.0"
3334
base64 = "0.13"
3435
cargo-registry-s3 = { path = "src/s3", version = "0.2.0" }
3536
chrono = { version = "0.4.0", features = ["serde"] }

src/controllers/krate/badges.rs

+39-33
Original file line numberDiff line numberDiff line change
@@ -21,51 +21,57 @@ pub fn maintenance(req: &mut dyn RequestExt) -> EndpointResult {
2121

2222
let krate = krate.unwrap();
2323

24-
let maintenance_badge = CrateBadge::belonging_to(&krate)
24+
let maintenance_badge: Option<CrateBadge> = CrateBadge::belonging_to(&krate)
2525
.select((badges::crate_id, badges::all_columns))
2626
.load::<CrateBadge>(&*conn)?
2727
.into_iter()
2828
.find(|cb| matches!(cb.badge, Badge::Maintenance { .. }));
2929

30-
if maintenance_badge.is_none() {
31-
return Ok(req.redirect(
32-
"https://img.shields.io/badge/maintenance-unknown-lightgrey.svg".to_owned(),
33-
));
34-
}
30+
let status = maintenance_badge
31+
.map(|it| match it.badge {
32+
Badge::Maintenance { status } => Some(status),
33+
_ => None,
34+
})
35+
.flatten();
3536

36-
let status = match maintenance_badge {
37-
Some(CrateBadge {
38-
badge: Badge::Maintenance { status },
39-
..
40-
}) => Some(status),
41-
_ => None,
42-
};
37+
let badge = generate_badge(status);
38+
39+
let response = Response::builder()
40+
.status(200)
41+
.body(Body::from_vec(badge.into_bytes()))
42+
.unwrap();
4343

44-
let status = status.unwrap();
44+
Ok(response)
45+
}
4546

47+
fn generate_badge(status: Option<MaintenanceStatus>) -> String {
4648
let message = match status {
47-
MaintenanceStatus::ActivelyDeveloped => "actively--developed",
48-
MaintenanceStatus::PassivelyMaintained => "passively--maintained",
49-
MaintenanceStatus::AsIs => "as--is",
50-
MaintenanceStatus::None => "unknown",
51-
MaintenanceStatus::Experimental => "experimental",
52-
MaintenanceStatus::LookingForMaintainer => "looking--for--maintainer",
53-
MaintenanceStatus::Deprecated => "deprecated",
49+
Some(MaintenanceStatus::ActivelyDeveloped) => "actively-developed",
50+
Some(MaintenanceStatus::PassivelyMaintained) => "passively-maintained",
51+
Some(MaintenanceStatus::AsIs) => "as-is",
52+
Some(MaintenanceStatus::None) => "unknown",
53+
Some(MaintenanceStatus::Experimental) => "experimental",
54+
Some(MaintenanceStatus::LookingForMaintainer) => "looking-for-maintainer",
55+
Some(MaintenanceStatus::Deprecated) => "deprecated",
56+
None => "unknown",
5457
};
5558

5659
let color = match status {
57-
MaintenanceStatus::ActivelyDeveloped => "brightgreen",
58-
MaintenanceStatus::PassivelyMaintained => "yellowgreen",
59-
MaintenanceStatus::AsIs => "yellow",
60-
MaintenanceStatus::None => "lightgrey",
61-
MaintenanceStatus::Experimental => "blue",
62-
MaintenanceStatus::LookingForMaintainer => "orange",
63-
MaintenanceStatus::Deprecated => "red",
60+
Some(MaintenanceStatus::ActivelyDeveloped) => "brightgreen",
61+
Some(MaintenanceStatus::PassivelyMaintained) => "yellowgreen",
62+
Some(MaintenanceStatus::AsIs) => "yellow",
63+
Some(MaintenanceStatus::None) => "lightgrey",
64+
Some(MaintenanceStatus::Experimental) => "blue",
65+
Some(MaintenanceStatus::LookingForMaintainer) => "orange",
66+
Some(MaintenanceStatus::Deprecated) => "red",
67+
None => "lightgrey",
68+
};
69+
70+
let badge_options = badge::BadgeOptions {
71+
subject: "maintenance".to_owned(),
72+
status: message.to_owned(),
73+
color: color.to_string(),
6474
};
6575

66-
let url = format!(
67-
"https://img.shields.io/badge/maintenance-{}-{}.svg",
68-
message, color
69-
);
70-
Ok(req.redirect(url))
76+
badge::Badge::new(badge_options).unwrap().to_svg()
7177
}

src/tests/all.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,7 @@ fn bad_resp(r: &mut AppResponse) -> Option<Bad> {
198198
Some(bad)
199199
}
200200

201-
fn json<T>(r: &mut AppResponse) -> T
202-
where
203-
for<'de> T: serde::Deserialize<'de>,
204-
{
201+
fn text(r: &mut AppResponse) -> String {
205202
use conduit::Body::*;
206203

207204
let mut body = Body::empty();
@@ -212,8 +209,15 @@ where
212209
File(_) => unimplemented!(),
213210
};
214211

215-
let s = std::str::from_utf8(&body).unwrap();
216-
match serde_json::from_str(s) {
212+
std::str::from_utf8(&body).unwrap().to_owned()
213+
}
214+
215+
fn json<T>(r: &mut AppResponse) -> T
216+
where
217+
for<'de> T: serde::Deserialize<'de>,
218+
{
219+
let s = text(r);
220+
match serde_json::from_str(&s) {
217221
Ok(t) => t,
218222
Err(e) => panic!("failed to decode: {:?}\n{}", e, s),
219223
}

src/tests/maintenance_badge.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,24 @@ fn set_up() -> MockAnonymousUser {
3131
fn crate_with_maintenance_badge() {
3232
let anon = set_up();
3333

34-
anon.get::<()>("/api/v1/crates/foo/maintenance.svg")
35-
.assert_status(StatusCode::FOUND)
36-
.assert_redirects_to(
37-
"https://img.shields.io/badge/maintenance-looking--for--maintainer-orange.svg",
38-
);
34+
let response = anon
35+
.get::<String>("/api/v1/crates/foo/maintenance.svg")
36+
.good_text();
37+
38+
assert!(response.contains("looking-for-maintainer"));
39+
assert!(response.contains("fill=\"orange\""));
3940
}
4041

4142
#[test]
4243
fn crate_without_maintenance_badge() {
4344
let anon = set_up();
4445

45-
anon.get::<()>("/api/v1/crates/bar/maintenance.svg")
46-
.assert_status(StatusCode::FOUND)
47-
.assert_redirects_to("https://img.shields.io/badge/maintenance-unknown-lightgrey.svg");
46+
let response = anon
47+
.get::<String>("/api/v1/crates/bar/maintenance.svg")
48+
.good_text();
49+
50+
assert!(response.contains("unknown"));
51+
assert!(response.contains("fill=\"lightgrey\""));
4852
}
4953

5054
#[test]

src/tests/util.rs

+10-14
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ impl Bad {
584584
/// A type providing helper methods for working with responses
585585
#[must_use]
586586
pub struct Response<T> {
587-
response: AppResponse,
587+
pub response: AppResponse,
588588
callback_on_good: Option<Box<dyn Fn(&T)>>,
589589
}
590590

@@ -606,6 +606,15 @@ where
606606
}
607607
}
608608

609+
/// Assert that the response is good and deserialize the message
610+
#[track_caller]
611+
pub fn good_text(mut self) -> String {
612+
if !self.response.status().is_success() {
613+
panic!("bad response: {:?}", self.response.status());
614+
}
615+
crate::text(&mut self.response).to_owned()
616+
}
617+
609618
/// Assert that the response is good and deserialize the message
610619
#[track_caller]
611620
pub fn good(mut self) -> T {
@@ -649,19 +658,6 @@ where
649658
.ends_with(target));
650659
self
651660
}
652-
653-
pub fn assert_redirects_to(&self, target: &str) -> &Self {
654-
assert_eq!(
655-
self.response
656-
.headers()
657-
.get(header::LOCATION)
658-
.unwrap()
659-
.to_str()
660-
.unwrap(),
661-
target
662-
);
663-
self
664-
}
665661
}
666662

667663
impl Response<()> {

0 commit comments

Comments
 (0)