From f96cd52a25a342b067941368c934d2403a20e4c0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 17 Jul 2025 15:01:58 +0200 Subject: [PATCH 1/2] Add clippy and rustfmt in rust toolchain components --- rust-toolchain.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e88baf106..7855e6d55 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] channel = "1.88.0" +components = ["rustfmt", "clippy"] From 8751acfcdd9f9e079f4708b4b24e4cf756383365 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 17 Jul 2025 15:12:11 +0200 Subject: [PATCH 2/2] Add search alias for Rust official crates --- src/web/releases.rs | 138 +++++++++++++++++++++++++++---- templates/releases/releases.html | 31 +++---- 2 files changed, 140 insertions(+), 29 deletions(-) diff --git a/src/web/releases.rs b/src/web/releases.rs index 2696f26c5..097bb56ec 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -50,6 +50,7 @@ pub struct Release { pub(crate) build_time: Option>, pub(crate) stars: i32, pub(crate) has_unyanked_releases: Option, + pub(crate) href: Option<&'static str>, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -124,6 +125,7 @@ pub(crate) async fn get_releases( build_time: row.get(5), stars: row.get::, _>(6).unwrap_or(0), has_unyanked_releases: None, + href: None, }) .try_collect() .await?) @@ -142,6 +144,20 @@ struct SearchResult { pub next_page: Option, } +fn rust_lib_release(name: &str, description: &str, href: &'static str) -> ReleaseStatus { + ReleaseStatus::Available(Release { + name: name.to_string(), + version: String::new(), + description: Some(description.to_string()), + build_time: None, + target_name: None, + rustdoc_status: false, + stars: 0, + has_unyanked_releases: None, + href: Some(href), + }) +} + /// Get the search results for a crate search query /// /// This delegates to the crates.io search API. @@ -149,6 +165,7 @@ async fn get_search_results( conn: &mut sqlx::PgConnection, registry: &RegistryApi, query_params: &str, + query: &str, ) -> Result { let crate::registry_api::Search { crates, meta } = registry.search(query_params).await?; @@ -206,28 +223,68 @@ async fn get_search_results( rustdoc_status: row.rustdoc_status.unwrap_or(false), stars: row.stars.unwrap_or(0), has_unyanked_releases: row.has_unyanked_releases, + href: None, }, ) }) .try_collect() .await?; + // start with the original names from crates.io to keep the original ranking, + // extend with the release/build information from docs.rs + // Crates that are not on docs.rs yet will not be returned. + let mut results = Vec::new(); + match query { + "std" | "libstd" => { + results.push(rust_lib_release( + "std", + "Rust standard library", + "https://doc.rust-lang.org/stable/std", + )); + } + "core" | "libcore" => { + results.push(rust_lib_release( + "core", + "Rust core library", + "https://doc.rust-lang.org/stable/core", + )); + } + "alloc" | "liballoc" => { + results.push(rust_lib_release( + "core", + "Rust alloc library", + "https://doc.rust-lang.org/stable/alloc", + )); + } + "proc_macro" | "proc-macro" | "libproc_macro" | "libproc-macro" => { + results.push(rust_lib_release( + "proc_macro", + "Rust proc_macro library", + "https://doc.rust-lang.org/stable/proc_macro", + )); + } + "test" | "libtest" => { + results.push(rust_lib_release( + "test", + "Rust test library", + "https://doc.rust-lang.org/stable/test", + )); + } + _ => {} + } + let names: Vec = Arc::into_inner(names).expect("Arc still borrowed in `get_search_results`"); + results.extend(names.into_iter().map(|name| { + if let Some(release) = crates.remove(&name) { + ReleaseStatus::Available(release) + } else { + ReleaseStatus::NotAvailable(name) + } + })); + Ok(SearchResult { - // start with the original names from crates.io to keep the original ranking, - // extend with the release/build information from docs.rs - // Crates that are not on docs.rs yet will not be returned. - results: names - .into_iter() - .map(|name| { - if let Some(release) = crates.remove(&name) { - ReleaseStatus::Available(release) - } else { - ReleaseStatus::NotAvailable(name) - } - }) - .collect(), + results, prev_page: meta.prev_page, next_page: meta.next_page, }) @@ -589,7 +646,7 @@ pub(crate) async fn search_handler( } } - get_search_results(&mut conn, ®istry, query_params).await? + get_search_results(&mut conn, ®istry, query_params, "").await? } else if !query.is_empty() { let query_params: String = form_urlencoded::Serializer::new(String::new()) .append_pair("q", &query) @@ -597,7 +654,7 @@ pub(crate) async fn search_handler( .append_pair("per_page", &RELEASES_IN_RELEASES.to_string()) .finish(); - get_search_results(&mut conn, ®istry, &query_params).await? + get_search_results(&mut conn, ®istry, &query_params, &query).await? } else { return Err(AxumNope::NoResults); }; @@ -2231,4 +2288,55 @@ mod tests { Ok(()) }); } + + #[test] + fn test_search_std() { + async_wrapper(|env| async move { + let web = env.web_app().await; + + async fn inner(web: &axum::Router, krate: &str) -> Result<(), anyhow::Error> { + let full = kuchikiki::parse_html().one( + web.get(&format!("/releases/search?query={krate}")) + .await? + .text() + .await?, + ); + let items = full + .select("ul a.release") + .expect("missing list items") + .collect::>(); + + // empty because expand_rebuild_queue is not set + let item_element = items.first().unwrap(); + let item = item_element.as_node(); + assert_eq!( + item.select(".name") + .unwrap() + .next() + .unwrap() + .text_contents(), + "std" + ); + assert_eq!( + item.select(".description") + .unwrap() + .next() + .unwrap() + .text_contents(), + "Rust standard library", + ); + assert_eq!( + item_element.attributes.borrow().get("href").unwrap(), + "https://doc.rust-lang.org/stable/std" + ); + + Ok(()) + } + + inner(&web, "std").await?; + inner(&web, "libstd").await?; + + Ok(()) + }); + } } diff --git a/templates/releases/releases.html b/templates/releases/releases.html index 02f51f52f..49d32a43e 100644 --- a/templates/releases/releases.html +++ b/templates/releases/releases.html @@ -47,23 +47,26 @@ {%- else -%} {%- set release_version = release.version -%} {%- endif -%} - {% set link %} - {%- if release.rustdoc_status -%} + {%- set link -%} + {%- if let Some(href) = release.href -%} + {% set link = href.to_string() -%} + {%- elif release.rustdoc_status -%} {% set link = "/{}/{}/{}/"|format(release.name, release_version, release.target_name.as_deref().unwrap_or_default()) -%} {%- else -%} {% set link = "/crate/{}/{}"|format(release.name, release_version) -%} {%- endif -%} - - {#- -#} {%- endmatch -%} {%- endfor -%} - + {#- -#}