From bb505ca64815bf9a43ae8b657fd6e9f757f3f191 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 1 Dec 2023 11:10:27 +0100 Subject: [PATCH 1/2] fix changing versions not keeping the paths --- src/web/crate_details.rs | 66 ++++++++++++++++++++++++++++----- src/web/routes.rs | 12 ++++++ static/menu.js | 20 +++++++--- templates/rustdoc/releases.html | 2 - 4 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/web/crate_details.rs b/src/web/crate_details.rs index f0573d3b3..29c51b486 100644 --- a/src/web/crate_details.rs +++ b/src/web/crate_details.rs @@ -442,6 +442,8 @@ pub(crate) async fn crate_details_handler( struct ReleaseList { releases: Vec, crate_name: String, + inner_path: String, + target: String, } impl_axum_webpage! { @@ -452,28 +454,74 @@ impl_axum_webpage! { #[tracing::instrument] pub(crate) async fn get_all_releases( - Path(params): Path, + Path(params): Path, mut conn: DbConnection, ) -> AxumResult { - let crate_id = sqlx::query_scalar!( + let req_path: String = params.path.clone().unwrap_or_default(); + let req_path: Vec<&str> = req_path.split('/').collect(); + + let release_found = match_version(&mut conn, ¶ms.name, Some(¶ms.version)).await?; + trace!(?release_found, "found release"); + + let (version, _) = match release_found.version { + MatchSemver::Exact((version, _)) => (version.clone(), version), + MatchSemver::Latest((version, _)) => (version, "latest".to_string()), + MatchSemver::Semver(_) => return Err(AxumNope::VersionNotFound), + }; + + let row = sqlx::query!( "SELECT - crates.id AS crate_id + crates.id AS crate_id, + releases.doc_targets FROM crates - WHERE crates.name = $1;", + INNER JOIN releases on crates.id = releases.crate_id + WHERE crates.name = $1 and releases.version = $2;", params.name, + &version, ) .fetch_optional(&mut *conn) - .await?; + .await? + .ok_or(AxumNope::CrateNotFound)?; - let releases: Vec = if let Some(crate_id) = crate_id { - // get releases, sorted by semver - releases_for_crate(&mut conn, crate_id).await? + // get releases, sorted by semver + let releases: Vec = releases_for_crate(&mut conn, row.crate_id).await?; + + let doc_targets = MetaData::parse_doc_targets(row.doc_targets); + + let inner; + let (target, inner_path) = { + let mut inner_path = req_path.clone(); + + let target = if inner_path.len() > 1 + && doc_targets + .iter() + .any(|s| Some(s) == params.target.as_ref()) + { + inner_path.remove(0); + params.target.as_ref().unwrap() + } else { + "" + }; + + inner = inner_path.join("/"); + (target, inner.trim_end_matches('/')) + }; + let inner_path = if inner_path.is_empty() { + format!("{}/index.html", params.name) } else { - Vec::new() + format!("{}/{inner_path}", params.name) + }; + + let target = if target.is_empty() { + String::new() + } else { + format!("{target}/") }; let res = ReleaseList { releases, + target: target.to_string(), + inner_path, crate_name: params.name, }; Ok(res.into_response()) diff --git a/src/web/routes.rs b/src/web/routes.rs index 808be44e2..024d7ffd6 100644 --- a/src/web/routes.rs +++ b/src/web/routes.rs @@ -276,10 +276,22 @@ pub(super) fn build_axum_routes() -> AxumRouter { "/crate/:name/:version/menus/platforms/:target/", get_internal(super::crate_details::get_all_platforms), ) + .route( + "/crate/:name/:version/menus/releases/:target", + get_internal(super::crate_details::get_all_releases), + ) + .route( + "/crate/:name/:version/menus/releases/:target/*path", + get_internal(super::crate_details::get_all_releases), + ) .route( "/crate/:name/:version/menus/releases", get_internal(super::crate_details::get_all_releases), ) + .route( + "/crate/:name/:version/menus/releases/:target/", + get_internal(super::crate_details::get_all_releases), + ) .route( "/-/rustdoc.static/*path", get_internal(super::rustdoc::static_asset_handler), diff --git a/static/menu.js b/static/menu.js index c84e0eb3c..5842bd9a2 100644 --- a/static/menu.js +++ b/static/menu.js @@ -93,18 +93,28 @@ function loadAjaxMenu(menu, id, msg, path, extra) { currentMenu = newMenu; newMenu.className += " pure-menu-active"; backdrop.style.display = "block"; + + const parts = window.location.pathname.split("/"); + const startFrom = parts[1] === "crate" ? 4 : 3; + // We get everything except the first crate name and the version. + const innerPath = "/" + parts.slice(startFrom).join("/") + if (newMenu.querySelector("#releases-list")) { - loadAjaxMenu(newMenu, "releases-list", "release list", "releases", ""); + loadAjaxMenu( + newMenu, + "releases-list", + "release list", + "releases", + innerPath, + ); + } else if (newMenu.querySelector("#platforms")) { - const parts = window.location.pathname.split("/"); - const startFrom = parts[1] === "crate" ? 4 : 3; loadAjaxMenu( newMenu, "platforms", "platforms list", "platforms", - // We get everything except the first crate name and the version. - "/" + parts.slice(startFrom).join("/") + innerPath, ); } } diff --git a/templates/rustdoc/releases.html b/templates/rustdoc/releases.html index 7772dcbf9..5f2dd6c01 100644 --- a/templates/rustdoc/releases.html +++ b/templates/rustdoc/releases.html @@ -1,6 +1,4 @@ {% import "macros.html" as macros %} -{% set target = "" %} -{% set inner_path = releases[0].target_name ~ "/index.html" %}
    {{ macros::releases_list(name=crate_name, releases=releases, target=target, inner_path=inner_path) }}
From 24cc0a88837dc68f7f0db1afd234f0c9e9cac058 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 8 Dec 2023 07:19:22 +0100 Subject: [PATCH 2/2] add test for release-link partial --- ...4e72a4609ebfb0390708f5a61f7ab66db8808.json | 29 +++++++++++++++ ...30734fafc218f83ba856e2db6ddcfe423e72b.json | 22 ----------- src/web/rustdoc.rs | 37 +++++++++++++++---- 3 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 .sqlx/query-23457d8f5ff3b551d5c4039615b4e72a4609ebfb0390708f5a61f7ab66db8808.json delete mode 100644 .sqlx/query-3b5412eff4406994b584d50e5aa30734fafc218f83ba856e2db6ddcfe423e72b.json diff --git a/.sqlx/query-23457d8f5ff3b551d5c4039615b4e72a4609ebfb0390708f5a61f7ab66db8808.json b/.sqlx/query-23457d8f5ff3b551d5c4039615b4e72a4609ebfb0390708f5a61f7ab66db8808.json new file mode 100644 index 000000000..001f093f4 --- /dev/null +++ b/.sqlx/query-23457d8f5ff3b551d5c4039615b4e72a4609ebfb0390708f5a61f7ab66db8808.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n crates.id AS crate_id,\n releases.doc_targets\n FROM crates\n INNER JOIN releases on crates.id = releases.crate_id\n WHERE crates.name = $1 and releases.version = $2;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "crate_id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "doc_targets", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "23457d8f5ff3b551d5c4039615b4e72a4609ebfb0390708f5a61f7ab66db8808" +} diff --git a/.sqlx/query-3b5412eff4406994b584d50e5aa30734fafc218f83ba856e2db6ddcfe423e72b.json b/.sqlx/query-3b5412eff4406994b584d50e5aa30734fafc218f83ba856e2db6ddcfe423e72b.json deleted file mode 100644 index 05620b5c5..000000000 --- a/.sqlx/query-3b5412eff4406994b584d50e5aa30734fafc218f83ba856e2db6ddcfe423e72b.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n crates.id AS crate_id\n FROM crates\n WHERE crates.name = $1;", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "crate_id", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "3b5412eff4406994b584d50e5aa30734fafc218f83ba856e2db6ddcfe423e72b" -} diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs index a9bf4fde0..17e903690 100644 --- a/src/web/rustdoc.rs +++ b/src/web/rustdoc.rs @@ -2229,6 +2229,15 @@ mod test { }) } + fn parse_release_links_from_menu(body: &str) -> Vec { + kuchikiki::parse_html() + .one(body) + .select(r#"ul > li > a"#) + .expect("invalid selector") + .map(|elem| elem.attributes.borrow().get("href").unwrap().to_string()) + .collect() + } + #[test_case(true)] #[test_case(false)] fn test_version_link_goes_to_docs(archive_storage: bool) { @@ -2244,6 +2253,7 @@ mod test { .version("0.3.1") .archive_storage(archive_storage) .rustdoc_file("hexponent/index.html") + .rustdoc_file("hexponent/something.html") .create()?; // test rustdoc pages stay on the documentation @@ -2253,14 +2263,27 @@ mod test { .send()?; assert!(releases_response.status().is_success()); assert_cache_control(&releases_response, CachePolicy::ForeverInCdn, &env.config()); - let page = kuchikiki::parse_html().one(releases_response.text()?); - let selector = - r#"ul > li a[href="/crate/hexponent/0.3.1/target-redirect/hexponent/index.html"]"# - .to_string(); assert_eq!( - page.select(&selector).unwrap().count(), - 1, - "link to /target-redirect/ not found" + parse_release_links_from_menu(&releases_response.text()?), + vec![ + "/crate/hexponent/0.3.1/target-redirect/hexponent/index.html".to_owned(), + "/crate/hexponent/0.3.0/target-redirect/hexponent/index.html".to_owned(), + ] + ); + + // test if target-redirect inludes path + let releases_response = env + .frontend() + .get("/crate/hexponent/0.3.1/menus/releases/hexponent/something.html") + .send()?; + assert!(releases_response.status().is_success()); + assert_cache_control(&releases_response, CachePolicy::ForeverInCdn, &env.config()); + assert_eq!( + parse_release_links_from_menu(&releases_response.text()?), + vec![ + "/crate/hexponent/0.3.1/target-redirect/hexponent/something.html".to_owned(), + "/crate/hexponent/0.3.0/target-redirect/hexponent/something.html".to_owned(), + ] ); // test /crate pages stay on /crate