Skip to content

Commit 42321b6

Browse files
Fix crate pages handling target-redirect
1 parent 0d52ec8 commit 42321b6

File tree

3 files changed

+94
-16
lines changed

3 files changed

+94
-16
lines changed

src/web/crate_details.rs

+73-13
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use crate::{
1717
use anyhow::{Context, Result};
1818
use axum::{
1919
extract::{Extension, Path},
20-
http::Uri,
2120
response::{IntoResponse, Response as AxumResponse},
2221
};
2322
use chrono::{DateTime, Utc};
@@ -500,12 +499,11 @@ impl_axum_webpage! {
500499
}
501500

502501
#[tracing::instrument]
503-
pub(crate) async fn get_all_platforms(
502+
pub(crate) async fn get_all_platforms_inner(
504503
Path(params): Path<RustdocHtmlParams>,
505504
Extension(pool): Extension<Pool>,
506-
uri: Uri,
505+
is_crate_root: bool,
507506
) -> AxumResult<AxumResponse> {
508-
let is_crate_root = uri.path().starts_with("/-/menus/platforms/crate/");
509507
let req_path: String = params.path.unwrap_or_default();
510508
let req_path: Vec<&str> = req_path.split('/').collect();
511509

@@ -599,16 +597,23 @@ pub(crate) async fn get_all_platforms(
599597
.unwrap_or(&releases[0]);
600598

601599
// The path within this crate version's rustdoc output
600+
let inner;
602601
let (target, inner_path) = {
603602
let mut inner_path = req_path.clone();
604603

605-
let target = if inner_path.len() > 1 && doc_targets.iter().any(|s| s == inner_path[0]) {
606-
inner_path.remove(0)
604+
let target = if inner_path.len() > 1
605+
&& doc_targets
606+
.iter()
607+
.any(|s| Some(s) == params.target.as_ref())
608+
{
609+
inner_path.remove(0);
610+
params.target.as_ref().unwrap()
607611
} else {
608612
""
609613
};
610614

611-
(target, inner_path.join("/"))
615+
inner = inner_path.join("/");
616+
(target, inner.trim_end_matches('/'))
612617
};
613618
let inner_path = if inner_path.is_empty() {
614619
format!("{name}/index.html")
@@ -639,6 +644,21 @@ pub(crate) async fn get_all_platforms(
639644
Ok(res.into_response())
640645
}
641646

647+
pub(crate) async fn get_all_platforms_root(
648+
Path(mut params): Path<RustdocHtmlParams>,
649+
pool: Extension<Pool>,
650+
) -> AxumResult<AxumResponse> {
651+
params.path = None;
652+
get_all_platforms_inner(Path(params), pool, true).await
653+
}
654+
655+
pub(crate) async fn get_all_platforms(
656+
params: Path<RustdocHtmlParams>,
657+
pool: Extension<Pool>,
658+
) -> AxumResult<AxumResponse> {
659+
get_all_platforms_inner(params, pool, false).await
660+
}
661+
642662
#[cfg(test)]
643663
mod tests {
644664
use super::*;
@@ -1251,22 +1271,26 @@ mod tests {
12511271

12521272
#[test]
12531273
fn platform_links_are_direct_and_without_nofollow() {
1254-
fn check_links(response_text: String, ajax: bool, should_contain_redirect: bool) {
1255-
let platform_links: Vec<(String, String)> = kuchikiki::parse_html()
1274+
fn check_links(
1275+
response_text: String,
1276+
ajax: bool,
1277+
should_contain_redirect: bool,
1278+
) -> Vec<(String, String, String)> {
1279+
let platform_links: Vec<(String, String, String)> = kuchikiki::parse_html()
12561280
.one(response_text)
12571281
.select(&format!(r#"{}li a"#, if ajax { "" } else { "#platforms " }))
12581282
.expect("invalid selector")
12591283
.map(|el| {
12601284
let attributes = el.attributes.borrow();
12611285
let url = attributes.get("href").expect("href").to_string();
12621286
let rel = attributes.get("rel").unwrap_or("").to_string();
1263-
(url, rel)
1287+
(el.text_contents(), url, rel)
12641288
})
12651289
.collect();
12661290

12671291
assert_eq!(platform_links.len(), 2);
12681292

1269-
for (url, rel) in platform_links {
1293+
for (_, url, rel) in &platform_links {
12701294
assert_eq!(
12711295
url.contains("/target-redirect/"),
12721296
should_contain_redirect,
@@ -1278,25 +1302,53 @@ mod tests {
12781302
assert_eq!(rel, "nofollow");
12791303
}
12801304
}
1305+
platform_links
12811306
}
12821307

12831308
fn run_check_links(
12841309
env: &TestEnvironment,
12851310
url: &str,
12861311
extra: &str,
12871312
should_contain_redirect: bool,
1313+
) {
1314+
run_check_links_redir(
1315+
env,
1316+
url,
1317+
extra,
1318+
should_contain_redirect,
1319+
should_contain_redirect,
1320+
)
1321+
}
1322+
1323+
fn run_check_links_redir(
1324+
env: &TestEnvironment,
1325+
url: &str,
1326+
extra: &str,
1327+
should_contain_redirect: bool,
1328+
ajax_should_contain_redirect: bool,
12881329
) {
12891330
let response = env.frontend().get(url).send().unwrap();
12901331
assert!(response.status().is_success());
1291-
check_links(response.text().unwrap(), false, should_contain_redirect);
1332+
let list1 = check_links(response.text().unwrap(), false, should_contain_redirect);
12921333
// Same test with AJAX endpoint.
12931334
let response = env
12941335
.frontend()
12951336
.get(&format!("/-/menus/platforms{url}{extra}"))
12961337
.send()
12971338
.unwrap();
12981339
assert!(response.status().is_success());
1299-
check_links(response.text().unwrap(), true, should_contain_redirect);
1340+
let list2 = check_links(response.text().unwrap(), true, ajax_should_contain_redirect);
1341+
if should_contain_redirect == ajax_should_contain_redirect {
1342+
assert_eq!(list1, list2);
1343+
} else {
1344+
// If redirects differ, we only check platform names.
1345+
assert!(
1346+
list1.iter().zip(&list2).all(|(a, b)| a.0 == b.0),
1347+
"{:?} != {:?}",
1348+
list1,
1349+
list2,
1350+
);
1351+
}
13001352
}
13011353

13021354
wrapper(|env| {
@@ -1308,8 +1360,16 @@ mod tests {
13081360
.rustdoc_file("x86_64-pc-windows-msvc/dummy/struct.A.html")
13091361
.default_target("x86_64-unknown-linux-gnu")
13101362
.add_target("x86_64-pc-windows-msvc")
1363+
.source_file("README.md", b"storage readme")
13111364
.create()?;
13121365

1366+
// FIXME: For some reason, there are target-redirects on non-AJAX lists on docs.rs
1367+
// crate pages other than the "default" one.
1368+
run_check_links_redir(env, "/crate/dummy/0.4.0/features", "", true, false);
1369+
run_check_links_redir(env, "/crate/dummy/0.4.0/builds", "", true, false);
1370+
run_check_links_redir(env, "/crate/dummy/0.4.0/source/", "", true, false);
1371+
run_check_links_redir(env, "/crate/dummy/0.4.0/source/README.md", "", true, false);
1372+
13131373
run_check_links(env, "/crate/dummy/0.4.0", "", false);
13141374
run_check_links(env, "/dummy/latest/dummy", "/", true);
13151375
run_check_links(env, "/dummy/0.4.0/x86_64-pc-windows-msvc/dummy", "/", true);

src/web/routes.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,27 @@ pub(super) fn build_axum_routes() -> AxumRouter {
238238
)
239239
.route(
240240
"/-/menus/platforms/crate/:name/:version",
241-
get_internal(super::crate_details::get_all_platforms),
241+
get_internal(super::crate_details::get_all_platforms_root),
242+
)
243+
.route(
244+
"/-/menus/platforms/crate/:name/:version/features",
245+
get_internal(super::crate_details::get_all_platforms_root),
246+
)
247+
.route(
248+
"/-/menus/platforms/crate/:name/:version/builds",
249+
get_internal(super::crate_details::get_all_platforms_root),
250+
)
251+
.route(
252+
"/-/menus/platforms/crate/:name/:version/builds/*path",
253+
get_internal(super::crate_details::get_all_platforms_root),
254+
)
255+
.route(
256+
"/-/menus/platforms/crate/:name/:version/source/",
257+
get_internal(super::crate_details::get_all_platforms_root),
258+
)
259+
.route(
260+
"/-/menus/platforms/crate/:name/:version/source/*path",
261+
get_internal(super::crate_details::get_all_platforms_root),
242262
)
243263
.route(
244264
"/-/menus/platforms/crate/:name/:version/:target",

src/web/rustdoc.rs

-2
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,7 @@ pub(crate) struct RustdocHtmlParams {
357357
// both target and path are only used for matching the route.
358358
// The actual path is read from the request `Uri` because
359359
// we have some static filenames directly in the routes.
360-
#[allow(dead_code)]
361360
pub(crate) target: Option<String>,
362-
#[allow(dead_code)]
363361
pub(crate) path: Option<String>,
364362
}
365363

0 commit comments

Comments
 (0)