@@ -36,8 +36,12 @@ use super::cache::CachePolicy;
36
36
// Introduce SearchError as new error type
37
37
#[ derive( Debug , thiserror:: Error ) ]
38
38
pub enum SearchError {
39
- #[ error( "crates.io error : {0}" ) ]
39
+ #[ error( "got error from crates.io: {0}" ) ]
40
40
CratesIo ( String ) ,
41
+ #[ error( "missing releases in crates.io response" ) ]
42
+ MissingReleases ,
43
+ #[ error( "missing metadata in crates.io response" ) ]
44
+ MissingMetadata ,
41
45
#[ error( transparent) ]
42
46
Other ( #[ from] anyhow:: Error ) ,
43
47
}
@@ -169,7 +173,7 @@ async fn get_search_results(
169
173
let result = registry. search ( query_params) . await ;
170
174
let crate :: registry_api:: Search { crates, meta } = match result {
171
175
Ok ( results_from_search_request) => results_from_search_request,
172
- Err ( err) => return handle_registry_error ( err) ,
176
+ Err ( err) => return Err ( handle_registry_error ( err) ) ,
173
177
} ;
174
178
175
179
let names = Arc :: new (
@@ -254,18 +258,34 @@ async fn get_search_results(
254
258
}
255
259
256
260
// Categorize errors from registry
257
- fn handle_registry_error ( err : anyhow:: Error ) -> Result < SearchResult , SearchError > {
261
+ fn handle_registry_error ( err : anyhow:: Error ) -> SearchError {
258
262
// Capture crates.io API error
259
263
if let Some ( registry_request_error) = err. downcast_ref :: < reqwest:: Error > ( )
260
264
&& let Some ( status) = registry_request_error. status ( )
261
265
&& ( status. is_client_error ( ) || status. is_server_error ( ) )
262
266
{
263
- return Err ( SearchError :: CratesIo ( format ! (
267
+ return SearchError :: CratesIo ( format ! (
264
268
"crates.io returned {status}: {registry_request_error}"
265
- ) ) ) ;
269
+ ) ) ;
266
270
}
267
- // Move all other error types to this wrapper
268
- Err ( SearchError :: Other ( err) )
271
+
272
+ // Errors from bail!() in RegistryApi::search()
273
+ let msg = err. to_string ( ) ;
274
+ if msg. contains ( "missing releases in crates.io response" ) {
275
+ return SearchError :: MissingReleases ;
276
+ }
277
+ if msg. contains ( "missing metadata in crates.io response" ) {
278
+ return SearchError :: MissingMetadata ;
279
+ }
280
+ if msg. contains ( "got error from crates.io:" ) {
281
+ return SearchError :: CratesIo (
282
+ msg. trim_start_matches ( "got error from crates.io:" )
283
+ . trim ( )
284
+ . to_string ( ) ,
285
+ ) ;
286
+ }
287
+ // Move all other error to this fallback wrapper
288
+ SearchError :: Other ( err)
269
289
}
270
290
271
291
//Error message to gracefully display
@@ -654,12 +674,30 @@ pub(crate) async fn search_handler(
654
674
let search_result = match search_result {
655
675
Ok ( result) => result,
656
676
Err ( SearchError :: CratesIo ( error_message) ) => {
657
- // Return a user-friendly error response
658
- return Ok ( create_search_error_response ( query, sort_by, error_message) . into_response ( ) ) ;
677
+ return Ok ( create_search_error_response (
678
+ query,
679
+ sort_by,
680
+ format ! ( "We're having issues communicating with crates.io: {error_message}" ) ,
681
+ )
682
+ . into_response ( ) ) ;
683
+ }
684
+ Err ( SearchError :: MissingReleases ) => {
685
+ return Ok ( create_search_error_response (
686
+ query,
687
+ sort_by,
688
+ "missing releases in crates.io response" . to_string ( ) ,
689
+ )
690
+ . into_response ( ) ) ;
691
+ }
692
+ Err ( SearchError :: MissingMetadata ) => {
693
+ return Ok ( create_search_error_response (
694
+ query,
695
+ sort_by,
696
+ "missing metadata in crates.io response" . to_string ( ) ,
697
+ )
698
+ . into_response ( ) ) ;
659
699
}
660
700
Err ( SearchError :: Other ( err) ) => {
661
- // For other errors, propagate them normally
662
- // NOTE - Errrors that are not 400x or 500x will be logged to Sentry
663
701
return Err ( err. into ( ) ) ;
664
702
}
665
703
} ;
@@ -1275,7 +1313,7 @@ mod tests {
1275
1313
. await
1276
1314
. get ( "/releases/search?query=doesnt_matter_here" )
1277
1315
. await ?;
1278
- assert_eq ! ( response. status( ) , 500 ) ;
1316
+ assert_eq ! ( response. status( ) , http :: StatusCode :: SERVICE_UNAVAILABLE ) ;
1279
1317
1280
1318
assert ! (
1281
1319
response
@@ -2297,16 +2335,17 @@ mod tests {
2297
2335
#[ test]
2298
2336
fn test_create_search_error_response ( ) {
2299
2337
let response = create_search_error_response (
2300
- "test_query" . to_string ( ) ,
2301
- "relevance" . to_string ( ) ,
2302
- "Service temporarily unavailable" . to_string ( ) ,
2338
+ "test_query" . to_string ( ) . clone ( ) ,
2339
+ "relevance" . to_string ( ) . clone ( ) ,
2340
+ "Service temporarily unavailable" . to_string ( ) . clone ( ) ,
2303
2341
) ;
2304
2342
assert_eq ! (
2305
2343
response. title,
2306
2344
"Search service is not currently available: Service temporarily unavailable"
2307
2345
) ;
2308
2346
assert_eq ! ( response. status, http:: StatusCode :: SERVICE_UNAVAILABLE ) ;
2309
2347
assert_eq ! ( response. release_type, ReleaseType :: Search ) ;
2348
+ assert ! ( response. title. contains( "Service temporarily unavailable" ) ) ;
2310
2349
}
2311
2350
2312
2351
#[ test]
@@ -2328,7 +2367,15 @@ mod tests {
2328
2367
. await
2329
2368
. get ( "/releases/search?query=anything_goes_here" )
2330
2369
. await ?;
2331
- assert_eq ! ( response. status( ) , 503 ) ;
2370
+
2371
+ assert_eq ! ( response. status( ) , http:: StatusCode :: SERVICE_UNAVAILABLE ) ;
2372
+ let parse_html = kuchikiki:: parse_html ( ) . one ( response. text ( ) . await ?) ;
2373
+
2374
+ assert ! (
2375
+ parse_html
2376
+ . text_contents( )
2377
+ . contains( "We're having issues communicating with crates.io" )
2378
+ ) ;
2332
2379
Ok ( ( ) )
2333
2380
} )
2334
2381
}
0 commit comments