Skip to content

Commit 282d7f7

Browse files
committed
Generate maintenance badges via badge crate
1 parent c72814a commit 282d7f7

File tree

6 files changed

+115
-61
lines changed

6 files changed

+115
-61
lines changed

Cargo.lock

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
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

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ 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()
@@ -30,45 +30,51 @@ pub fn maintenance(req: &mut dyn RequestExt) -> EndpointResult {
3030
_ => false,
3131
});
3232

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

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

47-
let status = status.unwrap();
47+
Ok(response)
48+
}
4849

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

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

69-
let url = format!(
70-
"https://img.shields.io/badge/maintenance-{}-{}.svg",
71-
message, color
72-
);
73-
Ok(req.redirect(url))
79+
badge::Badge::new(badge_options).unwrap().to_svg()
7480
}

src/tests/all.rs

Lines changed: 10 additions & 6 deletions
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

Lines changed: 12 additions & 8 deletions
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

Lines changed: 10 additions & 14 deletions
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)