Skip to content

Commit 85cd7ca

Browse files
Load release list only when needed
1 parent 77eaf07 commit 85cd7ca

File tree

5 files changed

+139
-5
lines changed

5 files changed

+139
-5
lines changed

src/web/crate_details.rs

+32
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,38 @@ pub(crate) async fn crate_details_handler(
373373
Ok(res.into_response())
374374
}
375375

376+
#[tracing::instrument]
377+
pub(crate) async fn get_all_releases(
378+
Path(params): Path<CrateDetailHandlerParams>,
379+
Extension(pool): Extension<Pool>,
380+
) -> AxumResult<axum::extract::Json<Vec<Release>>> {
381+
let releases: Vec<Release> = spawn_blocking({
382+
let pool = pool.clone();
383+
let params = params.clone();
384+
move || {
385+
let mut conn = pool.get()?;
386+
let query = "
387+
SELECT
388+
crates.id AS crate_id
389+
FROM crates
390+
WHERE crates.name = $1;";
391+
392+
let rows = conn.query(query, &[&params.name])?;
393+
394+
let result = if rows.is_empty() {
395+
return Ok(Vec::new());
396+
} else {
397+
&rows[0]
398+
};
399+
// get releases, sorted by semver
400+
releases_for_crate(&mut *conn, result.get("crate_id"))
401+
}
402+
})
403+
.await?;
404+
405+
Ok(axum::extract::Json(releases))
406+
}
407+
376408
#[cfg(test)]
377409
mod tests {
378410
use super::*;

src/web/routes.rs

+4
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ pub(super) fn build_axum_routes() -> AxumRouter {
167167
"/crate/:name",
168168
get_internal(super::crate_details::crate_details_handler),
169169
)
170+
.route(
171+
"/:name/releases",
172+
get_internal(super::crate_details::get_all_releases),
173+
)
170174
.route_with_tsr(
171175
"/crate/:name/:version",
172176
get_internal(super::crate_details::crate_details_handler),

static/menu.js

+83
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,88 @@ const updateMenuPositionForSubMenu = (currentMenuSupplier) => {
55
subMenu?.style.setProperty('--menu-x', `${currentMenu.getBoundingClientRect().x}px`);
66
}
77

8+
function generateReleaseList(data, crateName) {
9+
const currentPath = window.location.pathname.split('/');
10+
const releaseData = document.getElementById('release-data');
11+
const innerPath = releaseData.getAttribute("data-inner-path");
12+
const target = releaseData.getAttribute("data-target");
13+
const releases = [];
14+
15+
for (const release of data) {
16+
let url;
17+
let retainFragment = "";
18+
19+
if (innerPath.length === "") {
20+
url = `/crate/${crateName}/${release.version}`;
21+
} else {
22+
url = `/crate/${crateName}/${release.version}/target-redirect/${target}${innerPath}`;
23+
retainFragment = `\ndata-fragment="retain"`;
24+
}
25+
let releaseName = `${crateName} - ${release.version}`
26+
let warning = null;
27+
28+
if (!release.is_library) {
29+
// If the release isn't a library, then display that warning.
30+
warning = `${releaseName} is not a library`;
31+
} else if (release.yanked && release.build_status !== null) {
32+
// If the release has been yanked and failed to build, display a warning.
33+
warning = `${releaseName} is yanked`;
34+
} else if (release.yanked) {
35+
// If the release has been yanked and failed to build, display a warning.
36+
warning = `${releaseName} is yanked and docs.rs failed to build it`;
37+
} else if (release.build_status === null) {
38+
// If the release failed to build, display a warning.
39+
warning = `docs.rs failed to build ${releaseName}`;
40+
}
41+
42+
let extraClass = "";
43+
let title = "";
44+
let extraText = "";
45+
46+
if (warning !== null) {
47+
extraClass = " warn";
48+
title = `\ntitle="${warning}"`;
49+
extraText = `\
50+
<span class="fa-svg fa-svg-fw " aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" \
51+
viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 \
52+
40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 \
53+
256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 \
54+
24-24V184c0-13.3-10.7-24-24-24zm32 224c0-17.7-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32s32-14.3 \
55+
32-32z"></path></svg></span> `;
56+
}
57+
58+
releases.push(`\
59+
<li class="pure-menu-item">
60+
<a href="${url}"
61+
rel="nofollow"
62+
class="pure-menu-link${extraClass}"${retainFragment}${title}
63+
>${extraText}${release["version"]}</a>
64+
</li>`);
65+
}
66+
document.getElementById('releases-list').innerHTML = `\
67+
<ul class="pure-menu-list">${releases.join('')}</ul>`;
68+
}
69+
70+
let loadReleases = function() {
71+
const crateName = window.location.pathname.split('/')[1];
72+
const xhttp = new XMLHttpRequest();
73+
xhttp.onreadystatechange = function() {
74+
if (xhttp.readyState !== XMLHttpRequest.DONE) {
75+
return;
76+
}
77+
if (xhttp.status === 200) {
78+
generateReleaseList(JSON.parse(xhttp.responseText), crateName);
79+
} else {
80+
console.error(`Failed to load release list: [${xhttp.status}] ${xhttp.responseText}`);
81+
document.getElementById('releases-list').innerHTML = "Failed to load release list";
82+
}
83+
};
84+
xhttp.open("GET", `/${crateName}/releases`, true);
85+
xhttp.send();
86+
// To prevent reloading the list unnecessarily.
87+
loadReleases = function() {};
88+
};
89+
890
// Allow menus to be open and used by keyboard.
991
(function() {
1092
var currentMenu;
@@ -53,6 +135,7 @@ const updateMenuPositionForSubMenu = (currentMenuSupplier) => {
53135
currentMenu = newMenu;
54136
newMenu.className += " pure-menu-active";
55137
backdrop.style.display = "block";
138+
loadReleases();
56139
}
57140
function menuOnClick(e) {
58141
if (this.getAttribute("href") != "#") {

templates/rustdoc/topbar.html

+3-5
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,9 @@
134134
<li class="pure-menu-heading">Versions</li>
135135

136136
<li class="pure-menu-item">
137-
<div class="pure-menu pure-menu-scrollable sub-menu" tabindex="-1">
138-
<ul class="pure-menu-list">
139-
{# Display all releases of this crate #}
140-
{{ macros::releases_list(name=krate.name, releases=krate.releases, target=target, inner_path=inner_path) }}
141-
</ul>
137+
<div class="pure-menu pure-menu-scrollable sub-menu" id="releases-list" tabindex="-1">
138+
<div class="hidden" id="release-data" data-inner-path="{{inner_path}}" data-target="{{target}}" ></div>
139+
<span class="rotate"></span>
142140
</div>
143141
</li>
144142
</ul>

templates/style/_navbar.scss

+17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ body {
1515
padding: 0;
1616
}
1717

18+
@keyframes rotating_text {
19+
from {
20+
transform: rotate(0deg);
21+
}
22+
to {
23+
transform: rotate(360deg);
24+
}
25+
}
26+
1827
div.nav-container {
1928
// Nothing is supposed to be over or hovering the top navbar. Maybe add a few others '('? :)
2029
z-index: 999;
@@ -324,6 +333,14 @@ div.nav-container {
324333
}
325334
}
326335
}
336+
337+
#releases-list {
338+
.rotate {
339+
display: inline-block;
340+
font-size: 30px;
341+
animation: rotating_text 2s linear infinite;
342+
}
343+
}
327344
}
328345

329346
#nav-search {

0 commit comments

Comments
 (0)