Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 80 additions & 8 deletions src/web/releases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Release>,
pub results: Vec<ReleaseStatus>,
pub prev_page: Option<String>,
pub next_page: Option<String>,
}
Expand Down Expand Up @@ -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<String, Release> = sqlx::query!(
let mut crates: HashMap<String, Release> = sqlx::query!(
r#"SELECT
crates.name,
releases.version,
Expand Down Expand Up @@ -206,14 +213,21 @@ async fn get_search_results(
.try_collect()
.await?;

let names: Vec<String> =
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,
Expand Down Expand Up @@ -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<Release>,
releases: Vec<ReleaseStatus>,
description: String,
release_type: ReleaseType,
show_next_page: bool,
Expand Down Expand Up @@ -355,7 +369,10 @@ pub(crate) async fn releases_handler(
);

Ok(ViewReleases {
releases,
releases: releases
.into_iter()
.map(ReleaseStatus::Available)
.collect::<Vec<_>>(),
description: description.into(),
release_type,
show_next_page,
Expand Down Expand Up @@ -407,7 +424,7 @@ pub(crate) async fn owner_handler(Path(owner): Path<String>) -> AxumResult<impl
#[derive(Debug, Clone, PartialEq)]
pub(super) struct Search {
pub(super) title: String,
pub(super) releases: Vec<Release>,
pub(super) releases: Vec<ReleaseStatus>,
pub(super) search_query: Option<String>,
pub(super) search_sort_by: Option<String>,
pub(super) previous_page_link: Option<String>,
Expand Down Expand Up @@ -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(())
})
}
}
98 changes: 54 additions & 44 deletions templates/releases/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,54 +30,64 @@
<ul>
{# TODO: If there are no releases, then display a message that says so #}
{%- for release in releases -%}
{%- set release_version -%}
{%- set has_unyanked_releases = release.has_unyanked_releases.unwrap_or(true) -%}
{%- if release_type == "search" && has_unyanked_releases -%}
{%- set release_version = "latest" -%}
{%- else -%}
{%- set release_version = release.version -%}
{%- endif -%}
{% set link %}
{%- if 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 -%}
<li>
<a href="{{ link|safe }}" class="release">
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-6-24 pure-u-md-5-24 name">
{{ release.name }}-{{ release.version }}
{% if !has_unyanked_releases %}
<span class="yanked" title="all releases of {{ release.name }} have been yanked">
{{ crate::icons::IconTrash.render_solid(false, false, "") }}
Yanked
</span>
{% endif %}
</div>

<div class="pure-u-1 pure-u-sm-14-24 pure-u-md-16-24 description">
{{ release.description.as_deref().unwrap_or_default() }}
{%- match release -%}
{%- when ReleaseStatus::NotAvailable(name) -%}
<div class="release">
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-6-24 pure-u-md-5-24 name not-available">{{ name }}</div>
<div class="pure-u-1 pure-u-sm-14-24 pure-u-md-16-24 description">Documentation not available on docs.rs</div>
</div>

{% if release_type == "owner" -%}
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date" {% if let Some(build_time) = release.build_time -%}
title="Published {{ build_time|timeformat }}" {%- endif -%}>
{{ release.stars }}
{{ crate::icons::IconStar.render_solid(false, false, "") }}
</div>
{%- elif let Some(build_time) = release.build_time -%}
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date"
title="{{ build_time.format("%FT%TZ") }}">
{{ build_time|timeformat }}
</div>
{%- when ReleaseStatus::Available(release) -%}
{%- set release_version -%}
{%- set has_unyanked_releases = release.has_unyanked_releases.unwrap_or(true) -%}
{%- if release_type == "search" && has_unyanked_releases -%}
{%- set release_version = "latest" -%}
{%- else -%}
{%- set release_version = release.version -%}
{%- endif -%}
{% set link %}
{%- if 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 -%}
<a href="{{ link|safe }}" class="release">
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-6-24 pure-u-md-5-24 name">
{{- release.name }}-{{ release.version }}
{%+ if !has_unyanked_releases -%}
<span class="yanked" title="all releases of {{ release.name }} have been yanked">
{{- crate::icons::IconTrash.render_solid(false, false, "") +}}
Yanked
</span>
{%- endif -%}
</div>
{%- else -%}
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date">
&mdash;

<div class="pure-u-1 pure-u-sm-14-24 pure-u-md-16-24 description">
{{- release.description.as_deref().unwrap_or_default() -}}
</div>
{%- endif %}
</div>
</a>

{%- if release_type == "owner" -%}
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date" {% if let Some(build_time) = release.build_time -%}
title="Published {{ build_time|timeformat }}" {%- endif -%}>
{{- release.stars +}}
{{ crate::icons::IconStar.render_solid(false, false, "") -}}
</div>
{%- elif let Some(build_time) = release.build_time -%}
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date"
title="{{ build_time.format("%FT%TZ") }}">
{{- build_time|timeformat -}}
</div>
{%- else -%}
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date">
&mdash;
</div>
{%- endif %}
</div>
</a>
{%- endmatch -%}
</li>
{%- endfor -%}
</ul>
Expand Down
4 changes: 2 additions & 2 deletions templates/releases/search_results.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
{%- endblock topbar -%}

{% block sort_by %}
<div id="search-select-nav">
<div id="search-select-nav">
<div class="item-end">
<span>Sort by</span>
<label for="nav-sort">
{{ crate::icons::IconList.render_solid(false, false, "") }}
</label>
{% set search_sort_by_val = search_sort_by.as_deref().unwrap_or_default() %}
<select form="nav-search-form" name="sort" id="nav-sort" aria-label="Find crate by the sort by select-box" tabindex="-1">
<select form="nav-search-form" name="sort" id="nav-sort" aria-label="Find crate by the sort by select-box" tabindex="-1">
<option value="relevance" {%- if search_sort_by_val == "relevance" %} selected="selected" {%- endif %}>Relevance</option>
<option value="downloads" {%- if search_sort_by_val == "downloads" %} selected="selected" {%- endif %}>All-Time Downloads</option>
<option value="recent-downloads" {%- if search_sort_by_val == "recent-downloads" %} selected="selected" {%- endif %}>Recent Downloads</option>
Expand Down
4 changes: 4 additions & 0 deletions templates/style/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ div.recent-releases-container {
}
}

.name.not-available {
color: var(--color-standard);
}

.name:hover {
overflow: visible;
white-space: normal;
Expand Down
Loading