Skip to content

Commit cea3de0

Browse files
committed
Serve /crate/latest/crate rather than redirect
1 parent e5d9879 commit cea3de0

File tree

9 files changed

+349
-59
lines changed

9 files changed

+349
-59
lines changed

src/web/build_details.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub fn build_details_handler(req: &mut Request) -> IronResult<Response> {
8181
};
8282

8383
BuildDetailsPage {
84-
metadata: cexpect!(req, MetaData::from_crate(&mut conn, name, version)),
84+
metadata: cexpect!(req, MetaData::from_crate(&mut conn, name, version, version)),
8585
build_details,
8686
}
8787
.into_response(req)

src/web/builds.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
4949
.last()
5050
.map_or(false, |segment| segment.ends_with(".json"));
5151

52-
let version =
52+
let (version, version_or_latest) =
5353
match match_version(&mut conn, name, req_version).and_then(|m| m.assume_exact())? {
54-
MatchSemver::Exact((version, _)) => version,
54+
MatchSemver::Exact((version, _)) => (version.clone(), version),
55+
MatchSemver::Latest((version, _)) => (version, "latest".to_string()),
5556

5657
MatchSemver::Semver((version, _)) => {
5758
let ext = if is_json { ".json" } else { "" };
@@ -117,7 +118,10 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
117118
Ok(resp)
118119
} else {
119120
BuildsPage {
120-
metadata: cexpect!(req, MetaData::from_crate(&mut conn, name, &version)),
121+
metadata: cexpect!(
122+
req,
123+
MetaData::from_crate(&mut conn, name, &version, &version_or_latest)
124+
),
121125
builds,
122126
limits,
123127
}
@@ -307,7 +311,7 @@ mod tests {
307311
}
308312

309313
#[test]
310-
fn latest_redirect() {
314+
fn latest_200() {
311315
wrapper(|env| {
312316
env.fake_release()
313317
.name("aquarelle")
@@ -332,7 +336,12 @@ mod tests {
332336
assert!(resp
333337
.url()
334338
.as_str()
335-
.ends_with("/crate/aquarelle/0.2.0/builds"));
339+
.ends_with("/crate/aquarelle/latest/builds"));
340+
let body = String::from_utf8(resp.bytes().unwrap().to_vec()).unwrap();
341+
assert!(body.contains("<a href=\"/crate/aquarelle/latest/features\""));
342+
assert!(body.contains("<a href=\"/crate/aquarelle/latest/builds\""));
343+
assert!(body.contains("<a href=\"/crate/aquarelle/latest/source/\""));
344+
assert!(body.contains("<a href=\"/crate/aquarelle/latest\""));
336345

337346
let resp_json = env
338347
.frontend()
@@ -341,7 +350,7 @@ mod tests {
341350
assert!(resp_json
342351
.url()
343352
.as_str()
344-
.ends_with("/crate/aquarelle/0.2.0/builds.json"));
353+
.ends_with("/crate/aquarelle/latest/builds.json"));
345354

346355
Ok(())
347356
});

src/web/crate_details.rs

+74-11
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ impl CrateDetails {
7979
conn: &mut Client,
8080
name: &str,
8181
version: &str,
82+
version_or_latest: &str,
8283
up: &RepositoryStatsUpdater,
8384
) -> Option<CrateDetails> {
8485
// get all stuff, I love you rustfmt
@@ -150,6 +151,7 @@ impl CrateDetails {
150151
let metadata = MetaData {
151152
name: krate.get("name"),
152153
version: krate.get("version"),
154+
version_or_latest: version_or_latest.to_string(),
153155
description: krate.get("description"),
154156
rustdoc_status: krate.get("rustdoc_status"),
155157
target_name: krate.get("target_name"),
@@ -281,16 +283,21 @@ pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> {
281283
let name = cexpect!(req, router.find("name"));
282284
let req_version = router.find("version");
283285

284-
let mut conn = extension!(req, Pool).get()?;
285-
286-
match match_version(&mut conn, name, req_version).and_then(|m| m.assume_exact())? {
287-
MatchSemver::Exact((version, _)) => {
288-
let updater = extension!(req, RepositoryStatsUpdater);
289-
let details = cexpect!(req, CrateDetails::new(&mut conn, name, &version, updater));
286+
if req_version == None {
287+
let url = ctry!(
288+
req,
289+
Url::parse(&format!("{}/crate/{}/latest", redirect_base(req), name,)),
290+
);
291+
return Ok(super::redirect(url));
292+
}
290293

291-
CrateDetailsPage { details }.into_response(req)
292-
}
294+
let mut conn = extension!(req, Pool).get()?;
293295

296+
let found_version =
297+
match_version(&mut conn, name, req_version).and_then(|m| m.assume_exact())?;
298+
let (version, version_or_latest) = match found_version {
299+
MatchSemver::Exact((version, _)) => (version.clone(), version),
300+
MatchSemver::Latest((version, _)) => (version, "latest".to_string()),
294301
MatchSemver::Semver((version, _)) => {
295302
let url = ctry!(
296303
req,
@@ -302,16 +309,24 @@ pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> {
302309
)),
303310
);
304311

305-
Ok(super::redirect(url))
312+
return Ok(super::redirect(url));
306313
}
307-
}
314+
};
315+
316+
let updater = extension!(req, RepositoryStatsUpdater);
317+
let details = cexpect!(
318+
req,
319+
CrateDetails::new(&mut conn, name, &version, &version_or_latest, updater)
320+
);
321+
322+
CrateDetailsPage { details }.into_response(req)
308323
}
309324

310325
#[cfg(test)]
311326
mod tests {
312327
use super::*;
313328
use crate::index::api::CrateOwner;
314-
use crate::test::{wrapper, TestDatabase};
329+
use crate::test::{assert_redirect, wrapper, TestDatabase};
315330
use anyhow::{Context, Error};
316331
use kuchiki::traits::TendrilSink;
317332
use std::collections::HashMap;
@@ -326,6 +341,7 @@ mod tests {
326341
&mut db.conn(),
327342
package,
328343
version,
344+
version,
329345
db.repository_stats_updater(),
330346
)
331347
.with_context(|| anyhow::anyhow!("could not fetch crate details"))?;
@@ -459,6 +475,7 @@ mod tests {
459475
&mut db.conn(),
460476
"foo",
461477
"0.2.0",
478+
"0.2.0",
462479
db.repository_stats_updater(),
463480
)
464481
.unwrap();
@@ -534,6 +551,7 @@ mod tests {
534551
&mut db.conn(),
535552
"foo",
536553
version,
554+
version,
537555
db.repository_stats_updater(),
538556
)
539557
.unwrap();
@@ -564,6 +582,7 @@ mod tests {
564582
&mut db.conn(),
565583
"foo",
566584
version,
585+
version,
567586
db.repository_stats_updater(),
568587
)
569588
.unwrap();
@@ -595,6 +614,7 @@ mod tests {
595614
&mut db.conn(),
596615
"foo",
597616
version,
617+
version,
598618
db.repository_stats_updater(),
599619
)
600620
.unwrap();
@@ -634,6 +654,7 @@ mod tests {
634654
&mut db.conn(),
635655
"foo",
636656
version,
657+
version,
637658
db.repository_stats_updater(),
638659
)
639660
.unwrap();
@@ -696,6 +717,7 @@ mod tests {
696717
&mut db.conn(),
697718
"foo",
698719
"0.0.1",
720+
"0.0.1",
699721
db.repository_stats_updater(),
700722
)
701723
.unwrap();
@@ -726,6 +748,7 @@ mod tests {
726748
&mut db.conn(),
727749
"foo",
728750
"0.0.1",
751+
"0.0.1",
729752
db.repository_stats_updater(),
730753
)
731754
.unwrap();
@@ -755,6 +778,7 @@ mod tests {
755778
&mut db.conn(),
756779
"foo",
757780
"0.0.1",
781+
"0.0.1",
758782
db.repository_stats_updater(),
759783
)
760784
.unwrap();
@@ -779,6 +803,7 @@ mod tests {
779803
&mut db.conn(),
780804
"foo",
781805
"0.0.1",
806+
"0.0.1",
782807
db.repository_stats_updater(),
783808
)
784809
.unwrap();
@@ -956,4 +981,42 @@ mod tests {
956981
Ok(())
957982
});
958983
}
984+
985+
#[test]
986+
fn latest_url() {
987+
wrapper(|env| {
988+
env.fake_release()
989+
.name("dummy")
990+
.version("0.4.0")
991+
.rustdoc_file("dummy/index.html")
992+
.rustdoc_file("x86_64-pc-windows-msvc/dummy/index.html")
993+
.default_target("x86_64-unknown-linux-gnu")
994+
.add_target("x86_64-pc-windows-msvc")
995+
.create()?;
996+
let web = env.frontend();
997+
998+
let resp = env.frontend().get("/crate/dummy/latest").send()?;
999+
assert!(resp.status().is_success());
1000+
assert!(resp.url().as_str().ends_with("/crate/dummy/latest"));
1001+
let body = String::from_utf8(resp.bytes().unwrap().to_vec()).unwrap();
1002+
assert!(body.contains("<a href=\"/crate/dummy/latest/features\""));
1003+
assert!(body.contains("<a href=\"/crate/dummy/latest/builds\""));
1004+
assert!(body.contains("<a href=\"/crate/dummy/latest/source/\""));
1005+
assert!(body.contains("<a href=\"/crate/dummy/latest\""));
1006+
1007+
assert_redirect("/crate/dummy/latest/", "/crate/dummy/latest", web)?;
1008+
assert_redirect("/crate/dummy", "/crate/dummy/latest", web)?;
1009+
1010+
let resp_json = env
1011+
.frontend()
1012+
.get("/crate/aquarelle/latest/builds.json")
1013+
.send()?;
1014+
assert!(resp_json
1015+
.url()
1016+
.as_str()
1017+
.ends_with("/crate/aquarelle/latest/builds.json"));
1018+
1019+
Ok(())
1020+
});
1021+
}
9591022
}

src/web/features.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
2929
let req_version = router.find("version");
3030

3131
let mut conn = extension!(req, Pool).get()?;
32-
let version =
32+
let (version, version_or_latest) =
3333
match match_version(&mut conn, name, req_version).and_then(|m| m.assume_exact())? {
34-
MatchSemver::Exact((version, _)) => version,
34+
MatchSemver::Exact((version, _)) => (version.clone(), version),
35+
MatchSemver::Latest((version, _)) => (version, "latest".to_string()),
3536

3637
MatchSemver::Semver((version, _)) => {
3738
let url = ctry!(
@@ -69,7 +70,10 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
6970
}
7071

7172
FeaturesPage {
72-
metadata: cexpect!(req, MetaData::from_crate(&mut conn, name, &version)),
73+
metadata: cexpect!(
74+
req,
75+
MetaData::from_crate(&mut conn, name, &version, &version_or_latest)
76+
),
7377
features,
7478
default_len,
7579
}
@@ -244,7 +248,7 @@ mod tests {
244248
}
245249

246250
#[test]
247-
fn latest_redirect() {
251+
fn latest_200() {
248252
wrapper(|env| {
249253
env.fake_release()
250254
.name("foo")
@@ -259,7 +263,11 @@ mod tests {
259263
.create()?;
260264

261265
let resp = env.frontend().get("/crate/foo/latest/features").send()?;
262-
assert!(resp.url().as_str().ends_with("/crate/foo/0.2.0/features"));
266+
assert!(resp.url().as_str().ends_with("/crate/foo/latest/features"));
267+
let body = String::from_utf8(resp.bytes().unwrap().to_vec()).unwrap();
268+
assert!(body.contains("<a href=\"/crate/foo/latest/builds\""));
269+
assert!(body.contains("<a href=\"/crate/foo/latest/source/\""));
270+
assert!(body.contains("<a href=\"/crate/foo/latest\""));
263271
Ok(())
264272
});
265273
}

0 commit comments

Comments
 (0)