Skip to content

Commit 4ffb875

Browse files
Add search alias for Rust official crates
1 parent db5df21 commit 4ffb875

File tree

3 files changed

+235
-43
lines changed

3 files changed

+235
-43
lines changed

src/web/releases.rs

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub struct Release {
5050
pub(crate) build_time: Option<DateTime<Utc>>,
5151
pub(crate) stars: i32,
5252
pub(crate) has_unyanked_releases: Option<bool>,
53+
pub(crate) href: Option<&'static str>,
5354
}
5455

5556
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -124,6 +125,7 @@ pub(crate) async fn get_releases(
124125
build_time: row.get(5),
125126
stars: row.get::<Option<i32>, _>(6).unwrap_or(0),
126127
has_unyanked_releases: None,
128+
href: None,
127129
})
128130
.try_collect()
129131
.await?)
@@ -142,13 +144,28 @@ struct SearchResult {
142144
pub next_page: Option<String>,
143145
}
144146

147+
fn rust_lib_release(name: &str, description: &str, href: &'static str) -> ReleaseStatus {
148+
ReleaseStatus::Available(Release {
149+
name: name.to_string(),
150+
version: String::new(),
151+
description: Some(description.to_string()),
152+
build_time: None,
153+
target_name: None,
154+
rustdoc_status: false,
155+
stars: 0,
156+
has_unyanked_releases: None,
157+
href: Some(href),
158+
})
159+
}
160+
145161
/// Get the search results for a crate search query
146162
///
147163
/// This delegates to the crates.io search API.
148164
async fn get_search_results(
149165
conn: &mut sqlx::PgConnection,
150166
registry: &RegistryApi,
151167
query_params: &str,
168+
query: &str,
152169
) -> Result<SearchResult, anyhow::Error> {
153170
let crate::registry_api::Search { crates, meta } = registry.search(query_params).await?;
154171

@@ -206,28 +223,38 @@ async fn get_search_results(
206223
rustdoc_status: row.rustdoc_status.unwrap_or(false),
207224
stars: row.stars.unwrap_or(0),
208225
has_unyanked_releases: row.has_unyanked_releases,
226+
href: None,
209227
},
210228
)
211229
})
212230
.try_collect()
213231
.await?;
214232

233+
// start with the original names from crates.io to keep the original ranking,
234+
// extend with the release/build information from docs.rs
235+
// Crates that are not on docs.rs yet will not be returned.
236+
let mut results = Vec::new();
237+
if let Some(super::rustdoc::OfficialCrateDescription {
238+
name,
239+
href,
240+
description,
241+
}) = super::rustdoc::DOC_RUST_LANG_ORG_REDIRECTS.get(query)
242+
{
243+
results.push(rust_lib_release(name, description, href))
244+
}
245+
215246
let names: Vec<String> =
216247
Arc::into_inner(names).expect("Arc still borrowed in `get_search_results`");
248+
results.extend(names.into_iter().map(|name| {
249+
if let Some(release) = crates.remove(&name) {
250+
ReleaseStatus::Available(release)
251+
} else {
252+
ReleaseStatus::NotAvailable(name)
253+
}
254+
}));
255+
217256
Ok(SearchResult {
218-
// start with the original names from crates.io to keep the original ranking,
219-
// extend with the release/build information from docs.rs
220-
// Crates that are not on docs.rs yet will not be returned.
221-
results: names
222-
.into_iter()
223-
.map(|name| {
224-
if let Some(release) = crates.remove(&name) {
225-
ReleaseStatus::Available(release)
226-
} else {
227-
ReleaseStatus::NotAvailable(name)
228-
}
229-
})
230-
.collect(),
257+
results,
231258
prev_page: meta.prev_page,
232259
next_page: meta.next_page,
233260
})
@@ -589,15 +616,15 @@ pub(crate) async fn search_handler(
589616
}
590617
}
591618

592-
get_search_results(&mut conn, &registry, query_params).await?
619+
get_search_results(&mut conn, &registry, query_params, "").await?
593620
} else if !query.is_empty() {
594621
let query_params: String = form_urlencoded::Serializer::new(String::new())
595622
.append_pair("q", &query)
596623
.append_pair("sort", &sort_by)
597624
.append_pair("per_page", &RELEASES_IN_RELEASES.to_string())
598625
.finish();
599626

600-
get_search_results(&mut conn, &registry, &query_params).await?
627+
get_search_results(&mut conn, &registry, &query_params, &query).await?
601628
} else {
602629
return Err(AxumNope::NoResults);
603630
};
@@ -2231,4 +2258,55 @@ mod tests {
22312258
Ok(())
22322259
});
22332260
}
2261+
2262+
#[test]
2263+
fn test_search_std() {
2264+
async_wrapper(|env| async move {
2265+
let web = env.web_app().await;
2266+
2267+
async fn inner(web: &axum::Router, krate: &str) -> Result<(), anyhow::Error> {
2268+
let full = kuchikiki::parse_html().one(
2269+
web.get(&format!("/releases/search?query={krate}"))
2270+
.await?
2271+
.text()
2272+
.await?,
2273+
);
2274+
let items = full
2275+
.select("ul a.release")
2276+
.expect("missing list items")
2277+
.collect::<Vec<_>>();
2278+
2279+
// empty because expand_rebuild_queue is not set
2280+
let item_element = items.first().unwrap();
2281+
let item = item_element.as_node();
2282+
assert_eq!(
2283+
item.select(".name")
2284+
.unwrap()
2285+
.next()
2286+
.unwrap()
2287+
.text_contents(),
2288+
"std"
2289+
);
2290+
assert_eq!(
2291+
item.select(".description")
2292+
.unwrap()
2293+
.next()
2294+
.unwrap()
2295+
.text_contents(),
2296+
"Rust standard library",
2297+
);
2298+
assert_eq!(
2299+
item_element.attributes.borrow().get("href").unwrap(),
2300+
"https://doc.rust-lang.org/stable/std"
2301+
);
2302+
2303+
Ok(())
2304+
}
2305+
2306+
inner(&web, "std").await?;
2307+
inner(&web, "libstd").await?;
2308+
2309+
Ok(())
2310+
});
2311+
}
22342312
}

src/web/rustdoc.rs

Lines changed: 125 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,129 @@ use tracing::{Instrument, debug, error, info_span, instrument, trace};
4343

4444
use super::extractors::PathFileExtension;
4545

46-
static DOC_RUST_LANG_ORG_REDIRECTS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
47-
HashMap::from([
48-
("alloc", "stable/alloc"),
49-
("core", "stable/core"),
50-
("proc_macro", "stable/proc_macro"),
51-
("proc-macro", "stable/proc_macro"),
52-
("std", "stable/std"),
53-
("test", "stable/test"),
54-
("rustc", "nightly/nightly-rustc"),
55-
("rustdoc", "nightly/nightly-rustc/rustdoc"),
56-
])
57-
});
46+
pub(crate) struct OfficialCrateDescription {
47+
pub(crate) name: &'static str,
48+
pub(crate) href: &'static str,
49+
pub(crate) description: &'static str,
50+
}
51+
52+
pub(crate) static DOC_RUST_LANG_ORG_REDIRECTS: Lazy<HashMap<&str, OfficialCrateDescription>> =
53+
Lazy::new(|| {
54+
HashMap::from([
55+
(
56+
"alloc",
57+
OfficialCrateDescription {
58+
name: "alloc",
59+
href: "https://doc.rust-lang.org/stable/alloc",
60+
description: "Rust alloc library",
61+
},
62+
),
63+
(
64+
"liballoc",
65+
OfficialCrateDescription {
66+
name: "alloc",
67+
href: "https://doc.rust-lang.org/stable/alloc",
68+
description: "Rust alloc library",
69+
},
70+
),
71+
(
72+
"core",
73+
OfficialCrateDescription {
74+
name: "core",
75+
href: "https://doc.rust-lang.org/stable/core",
76+
description: "Rust core library",
77+
},
78+
),
79+
(
80+
"libcore",
81+
OfficialCrateDescription {
82+
name: "core",
83+
href: "https://doc.rust-lang.org/stable/core",
84+
description: "Rust core library",
85+
},
86+
),
87+
(
88+
"proc_macro",
89+
OfficialCrateDescription {
90+
name: "proc_macro",
91+
href: "https://doc.rust-lang.org/stable/proc_macro",
92+
description: "Rust proc_macro library",
93+
},
94+
),
95+
(
96+
"libproc_macro",
97+
OfficialCrateDescription {
98+
name: "proc_macro",
99+
href: "https://doc.rust-lang.org/stable/proc_macro",
100+
description: "Rust proc_macro library",
101+
},
102+
),
103+
(
104+
"proc-macro",
105+
OfficialCrateDescription {
106+
name: "proc_macro",
107+
href: "https://doc.rust-lang.org/stable/proc_macro",
108+
description: "Rust proc_macro library",
109+
},
110+
),
111+
(
112+
"libproc-macro",
113+
OfficialCrateDescription {
114+
name: "proc_macro",
115+
href: "https://doc.rust-lang.org/stable/proc_macro",
116+
description: "Rust proc_macro library",
117+
},
118+
),
119+
(
120+
"std",
121+
OfficialCrateDescription {
122+
name: "std",
123+
href: "https://doc.rust-lang.org/stable/std",
124+
description: "Rust std library",
125+
},
126+
),
127+
(
128+
"libstd",
129+
OfficialCrateDescription {
130+
name: "std",
131+
href: "https://doc.rust-lang.org/stable/std",
132+
description: "Rust std library",
133+
},
134+
),
135+
(
136+
"test",
137+
OfficialCrateDescription {
138+
name: "test",
139+
href: "https://doc.rust-lang.org/stable/test",
140+
description: "Rust test library",
141+
},
142+
),
143+
(
144+
"libtest",
145+
OfficialCrateDescription {
146+
name: "test",
147+
href: "https://doc.rust-lang.org/stable/test",
148+
description: "Rust test library",
149+
},
150+
),
151+
(
152+
"rustc",
153+
OfficialCrateDescription {
154+
name: "rustc",
155+
href: "https://doc.rust-lang.org/nightly/nightly-rustc",
156+
description: "Rust rustc library",
157+
},
158+
),
159+
(
160+
"rustdoc",
161+
OfficialCrateDescription {
162+
name: "rustdoc",
163+
href: "https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc",
164+
description: "Rust rustdoc library",
165+
},
166+
),
167+
])
168+
});
58169

59170
#[derive(Debug, Clone, Deserialize)]
60171
pub(crate) struct RustdocRedirectorParams {
@@ -151,10 +262,10 @@ pub(crate) async fn rustdoc_redirector_handler(
151262
None => (params.name.to_string(), None),
152263
};
153264

154-
if let Some(inner_path) = DOC_RUST_LANG_ORG_REDIRECTS.get(crate_name.as_str()) {
265+
if let Some(description) = DOC_RUST_LANG_ORG_REDIRECTS.get(crate_name.as_str()) {
155266
return Ok(redirect_to_doc(
156267
&query_pairs,
157-
format!("https://doc.rust-lang.org/{inner_path}/"),
268+
description.href.to_string(),
158269
CachePolicy::ForeverInCdnAndStaleInBrowser,
159270
path_in_crate.as_deref(),
160271
)?

templates/releases/releases.html

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,26 @@
4747
{%- else -%}
4848
{%- set release_version = release.version -%}
4949
{%- endif -%}
50-
{% set link %}
51-
{%- if release.rustdoc_status -%}
50+
{%- set link -%}
51+
{%- if let Some(href) = release.href -%}
52+
{% set link = href.to_string() -%}
53+
{%- elif release.rustdoc_status -%}
5254
{% set link = "/{}/{}/{}/"|format(release.name, release_version, release.target_name.as_deref().unwrap_or_default()) -%}
5355
{%- else -%}
5456
{% set link = "/crate/{}/{}"|format(release.name, release_version) -%}
5557
{%- endif -%}
56-
<a href="{{ link|safe }}" class="release">
57-
<div class="pure-g">
58+
<a href="{{ link|safe }}" class="release"> {#- -#}
59+
<div class="pure-g"> {#- -#}
5860
<div class="pure-u-1 pure-u-sm-6-24 pure-u-md-5-24 name">
59-
{{- release.name }}-{{ release.version }}
60-
{%+ if !has_unyanked_releases -%}
61+
{{- release.name -}}
62+
{%- if !release.version.is_empty() -%}-{{ release.version }}{% endif -%}
63+
{%- if !has_unyanked_releases ~%}
6164
<span class="yanked" title="all releases of {{ release.name }} have been yanked">
62-
{{- crate::icons::IconTrash.render_solid(false, false, "") +}}
65+
{{- crate::icons::IconTrash.render_solid(false, false, "") ~}}
6366
Yanked
6467
</span>
6568
{%- endif -%}
66-
</div>
69+
</div> {#- -#}
6770

6871
<div class="pure-u-1 pure-u-sm-14-24 pure-u-md-16-24 description">
6972
{{- release.description.as_deref().unwrap_or_default() -}}
@@ -72,25 +75,25 @@
7275
{%- if release_type == "owner" -%}
7376
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date" {% if let Some(build_time) = release.build_time -%}
7477
title="Published {{ build_time|timeformat }}" {%- endif -%}>
75-
{{- release.stars +}}
78+
{{- release.stars ~}}
7679
{{ crate::icons::IconStar.render_solid(false, false, "") -}}
7780
</div>
7881
{%- elif let Some(build_time) = release.build_time -%}
79-
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date"
82+
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date" {#~ -#}
8083
title="{{ build_time.format("%FT%TZ") }}">
8184
{{- build_time|timeformat -}}
8285
</div>
8386
{%- else -%}
8487
<div class="pure-u-1 pure-u-sm-4-24 pure-u-md-3-24 date">
85-
&mdash;
88+
{%- if release.href.is_none() %}&mdash;{% endif -%}
8689
</div>
87-
{%- endif %}
88-
</div>
90+
{%- endif -%}
91+
</div> {#- -#}
8992
</a>
9093
{%- endmatch -%}
9194
</li>
9295
{%- endfor -%}
93-
</ul>
96+
</ul> {#- -#}
9497

9598
<div class="pagination">
9699
{% block pagination %}

0 commit comments

Comments
 (0)