Skip to content

Add search alias for Rust official crates #2868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[toolchain]
channel = "1.88.0"
components = ["rustfmt", "clippy"]
138 changes: 123 additions & 15 deletions src/web/releases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct Release {
pub(crate) build_time: Option<DateTime<Utc>>,
pub(crate) stars: i32,
pub(crate) has_unyanked_releases: Option<bool>,
pub(crate) href: Option<&'static str>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -124,6 +125,7 @@ pub(crate) async fn get_releases(
build_time: row.get(5),
stars: row.get::<Option<i32>, _>(6).unwrap_or(0),
has_unyanked_releases: None,
href: None,
})
.try_collect()
.await?)
Expand All @@ -142,13 +144,28 @@ struct SearchResult {
pub next_page: Option<String>,
}

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.
async fn get_search_results(
conn: &mut sqlx::PgConnection,
registry: &RegistryApi,
query_params: &str,
query: &str,
) -> Result<SearchResult, anyhow::Error> {
let crate::registry_api::Search { crates, meta } = registry.search(query_params).await?;

Expand Down Expand Up @@ -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<String> =
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,
})
Expand Down Expand Up @@ -589,15 +646,15 @@ pub(crate) async fn search_handler(
}
}

get_search_results(&mut conn, &registry, query_params).await?
get_search_results(&mut conn, &registry, query_params, "").await?
} else if !query.is_empty() {
let query_params: String = form_urlencoded::Serializer::new(String::new())
.append_pair("q", &query)
.append_pair("sort", &sort_by)
.append_pair("per_page", &RELEASES_IN_RELEASES.to_string())
.finish();

get_search_results(&mut conn, &registry, &query_params).await?
get_search_results(&mut conn, &registry, &query_params, &query).await?
} else {
return Err(AxumNope::NoResults);
};
Expand Down Expand Up @@ -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::<Vec<_>>();

// 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(())
});
}
}
31 changes: 17 additions & 14 deletions templates/releases/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 -%}
<a href="{{ link|safe }}" class="release">
<div class="pure-g">
<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 -%}
{{- release.name -}}
{%- if !release.version.is_empty() -%}-{{ release.version }}{% endif -%}
{%- if !has_unyanked_releases ~%}
<span class="yanked" title="all releases of {{ release.name }} have been yanked">
{{- crate::icons::IconTrash.render_solid(false, false, "") +}}
{{- crate::icons::IconTrash.render_solid(false, false, "") ~}}
Yanked
</span>
{%- endif -%}
</div>
</div> {#- -#}

<div class="pure-u-1 pure-u-sm-14-24 pure-u-md-16-24 description">
{{- release.description.as_deref().unwrap_or_default() -}}
Expand All @@ -72,25 +75,25 @@
{%- 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 +}}
{{- 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"
<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;
{%- if release.href.is_none() %}&mdash;{% endif -%}
</div>
{%- endif %}
</div>
{%- endif -%}
</div> {#- -#}
</a>
{%- endmatch -%}
</li>
{%- endfor -%}
</ul>
</ul> {#- -#}

<div class="pagination">
{% block pagination %}
Expand Down
Loading