diff --git a/src/web/releases.rs b/src/web/releases.rs index eed4cf075..5af8af930 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -130,8 +130,15 @@ pub(crate) async fn get_releases( .await?) } +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum ReleaseStatus { + Available(Release), + /// Only contains the crate name. + NotAvailable(String), +} + struct SearchResult { - pub results: Vec, + pub results: Vec, pub prev_page: Option, pub next_page: Option, } @@ -160,7 +167,7 @@ async fn get_search_results( // So for now we are using the version with the youngest release_time. // This is different from all other release-list views where we show // our latest build. - let crates: HashMap = sqlx::query!( + let mut crates: HashMap = sqlx::query!( r#"SELECT crates.name, releases.version, @@ -206,14 +213,21 @@ async fn get_search_results( .try_collect() .await?; + let names: Vec = + Arc::into_inner(names).expect("Arc still borrowed in `get_search_results`"); 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 - .iter() - .filter_map(|name| crates.get(name)) - .cloned() + .into_iter() + .map(|name| { + if let Some(release) = crates.remove(&name) { + ReleaseStatus::Available(release) + } else { + ReleaseStatus::NotAvailable(name) + } + }) .collect(), prev_page: meta.prev_page, next_page: meta.next_page, @@ -269,7 +283,7 @@ pub(crate) async fn releases_feed_handler(mut conn: DbConnection) -> AxumResult< #[template(path = "releases/releases.html")] #[derive(Debug, Clone, PartialEq, Eq)] struct ViewReleases { - releases: Vec, + releases: Vec, description: String, release_type: ReleaseType, show_next_page: bool, @@ -355,7 +369,10 @@ pub(crate) async fn releases_handler( ); Ok(ViewReleases { - releases, + releases: releases + .into_iter() + .map(ReleaseStatus::Available) + .collect::>(), description: description.into(), release_type, show_next_page, @@ -407,7 +424,7 @@ pub(crate) async fn owner_handler(Path(owner): Path) -> AxumResult, + pub(super) releases: Vec, pub(super) search_query: Option, pub(super) search_sort_by: Option, pub(super) previous_page_link: Option, @@ -2083,4 +2100,59 @@ mod tests { Ok(()) }); } + + #[test] + fn crates_not_on_docsrs() { + async_wrapper(|env| async move { + let mut crates_io = mockito::Server::new_async().await; + env.override_config(|config| { + config.registry_api_host = crates_io.url().parse().unwrap(); + }); + + let web = env.web_app().await; + env.async_fake_release() + .await + .name("some_random_crate") + .create_async() + .await?; + + let _m = crates_io + .mock("GET", "/api/v1/crates") + .match_query(Matcher::AllOf(vec![ + Matcher::UrlEncoded("q".into(), "some_random_crate".into()), + Matcher::UrlEncoded("per_page".into(), "30".into()), + ])) + .with_status(200) + .with_header("content-type", "application/json") + .with_body( + json!({ + "crates": [ + { "name": "some_random_crate" }, + { "name": "some_random_crate2" }, + { "name": "some_random_crate3" }, + ], + "meta": { + "next_page": "null", + "prev_page": "null", + } + }) + .to_string(), + ) + .create_async() + .await; + + let response = web.get("/releases/search?query=some_random_crate").await?; + assert!(response.status().is_success()); + + let page = kuchikiki::parse_html().one(response.text().await?); + + assert_eq!(page.select("div.name.not-available").unwrap().count(), 2); + assert_eq!( + page.select("div.name:not(.not-available)").unwrap().count(), + 1 + ); + + Ok(()) + }) + } } diff --git a/templates/releases/releases.html b/templates/releases/releases.html index 21e46da66..02f51f52f 100644 --- a/templates/releases/releases.html +++ b/templates/releases/releases.html @@ -30,54 +30,64 @@ diff --git a/templates/releases/search_results.html b/templates/releases/search_results.html index 82a391862..f411d3267 100644 --- a/templates/releases/search_results.html +++ b/templates/releases/search_results.html @@ -11,14 +11,14 @@ {%- endblock topbar -%} {% block sort_by %} -
+
Sort by {% set search_sort_by_val = search_sort_by.as_deref().unwrap_or_default() %} - diff --git a/templates/style/style.scss b/templates/style/style.scss index 27d73b68c..297b87940 100644 --- a/templates/style/style.scss +++ b/templates/style/style.scss @@ -331,6 +331,10 @@ div.recent-releases-container { } } + .name.not-available { + color: var(--color-standard); + } + .name:hover { overflow: visible; white-space: normal;