@@ -11,8 +11,11 @@ use log::{debug, info, warn};
11
11
use octocrab:: Octocrab ;
12
12
use regex:: Regex ;
13
13
use std:: collections:: { HashMap , HashSet } ;
14
+ use std:: fs;
15
+ use std:: path:: { Path , PathBuf } ;
14
16
use std:: process:: Command ;
15
17
use std:: rc:: Rc ;
18
+ use toml:: Value ;
16
19
17
20
/// Represents a clippy annotation in code
18
21
#[ derive( Debug , Clone ) ]
@@ -247,39 +250,74 @@ fn count_annotations_by_rule(annotations: &[ClippyAnnotation]) -> HashMap<Rc<Str
247
250
counts
248
251
}
249
252
250
- /// Get crate information for a given file path
251
- // TODO: EK - is this the right way to determine crate names?
253
+ /// Get crate information for a given file path by finding the closest Cargo.toml
252
254
fn get_crate_for_file ( file_path : & str ) -> String {
253
- // Simple heuristic: use the first directory as the crate name
254
- // For files in src/ directory, use the parent directory
255
- // For files in the root, use "root"
255
+ // Convert to Path for easier manipulation
256
+ let path = Path :: new ( file_path) ;
256
257
257
- let path_parts: Vec < & str > = file_path. split ( '/' ) . collect ( ) ;
258
+ // Try to find the closest Cargo.toml file
259
+ if let Some ( cargo_toml_path) = find_closest_cargo_toml ( path) {
260
+ if let Some ( crate_name) = extract_package_name ( & cargo_toml_path) {
261
+ return crate_name;
262
+ }
263
+ }
264
+
265
+ // Fallback to the old heuristic if Cargo.toml can't be found or parsed
266
+ "unknown-crate" . to_owned ( )
267
+ }
258
268
259
- if path_parts. is_empty ( ) {
260
- return "unknown" . to_owned ( ) ;
269
+ /// Find the closest Cargo.toml file by traversing up the directory tree
270
+ fn find_closest_cargo_toml ( mut path : & Path ) -> Option < PathBuf > {
271
+ // Start with the directory containing the file
272
+ if !path. is_dir ( ) {
273
+ path = path. parent ( ) ?;
261
274
}
262
275
263
- // Handle common project structures
264
- if path_parts . len ( ) > 1 {
265
- // If it's in "src" or "tests" folder, use the parent directory
266
- if path_parts [ 0 ] == "src" || path_parts [ 0 ] == "tests" {
267
- return "root" . to_owned ( ) ;
276
+ // Traverse up the directory tree
277
+ loop {
278
+ let cargo_path = path . join ( "Cargo.toml" ) ;
279
+ if cargo_path . exists ( ) {
280
+ return Some ( cargo_path ) ;
268
281
}
269
282
270
- // If it's in a nested crate structure like crates/foo/src
271
- if path_parts[ 0 ] == "crates" && path_parts. len ( ) > 2 {
272
- return path_parts[ 1 ] . to_owned ( ) ;
283
+ // Check if we've reached the root
284
+ let parent = path. parent ( ) ?;
285
+ if parent == path {
286
+ // We've reached the root without finding Cargo.toml
287
+ return None ;
273
288
}
274
289
275
- // If it's in a workspace pattern like foo/src
276
- if path_parts. len ( ) > 1 && ( path_parts[ 1 ] == "src" || path_parts[ 1 ] == "tests" ) {
277
- return path_parts[ 0 ] . to_owned ( ) ;
278
- }
290
+ // Move up one directory
291
+ path = parent;
279
292
}
293
+ }
280
294
281
- // Default: use first directory name
282
- path_parts[ 0 ] . to_owned ( )
295
+ /// Extract package name from Cargo.toml
296
+ fn extract_package_name ( cargo_toml_path : & Path ) -> Option < String > {
297
+ // Read the Cargo.toml file
298
+ let content = match fs:: read_to_string ( cargo_toml_path) {
299
+ Ok ( content) => content,
300
+ Err ( e) => {
301
+ log:: warn!( "Failed to read {}: {}" , cargo_toml_path. display( ) , e) ;
302
+ return None ;
303
+ }
304
+ } ;
305
+
306
+ // Parse the TOML
307
+ let toml_value: Value = match content. parse ( ) {
308
+ Ok ( value) => value,
309
+ Err ( e) => {
310
+ log:: warn!( "Failed to parse {}: {}" , cargo_toml_path. display( ) , e) ;
311
+ return None ;
312
+ }
313
+ } ;
314
+
315
+ // Extract the package name
316
+ toml_value
317
+ . get ( "package" ) ?
318
+ . get ( "name" ) ?
319
+ . as_str ( )
320
+ . map ( |s| s. to_string ( ) )
283
321
}
284
322
285
323
/// Count annotations by crate
@@ -421,7 +459,7 @@ fn get_branch_content(file: &str, branch: &str) -> String {
421
459
// "#;
422
460
//
423
461
// let regex =
424
- //
462
+ //
425
463
// Regex::new(r"#\s*\[\s*allow\s*\(\s*clippy\s*::\s*(unwrap_used|expect_used)\s*\)\s*\]")
426
464
// .unwrap();
427
465
// let mut rule_cache = HashMap::new();
0 commit comments