@@ -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.
148164async 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}
0 commit comments