Skip to content

Commit e2281a8

Browse files
committed
wip: combine owner and tag resolvers
1 parent f4ac229 commit e2281a8

File tree

4 files changed

+121
-19
lines changed

4 files changed

+121
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ custom_config.toml
66
.DS_Store
77
example-repo
88
.codeowners.cache
9+
perf.*

src/core/cache.rs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,49 @@
11
use std::io::{Read, Write};
22
use std::path::{Path, PathBuf};
33

4+
use rayon::prelude::*;
5+
use rayon::slice::ParallelSlice;
6+
47
use super::common::{collect_owners, collect_tags, get_repo_hash};
58
use super::owner_resolver::find_owners_for_file;
69
use super::parse::parse_repo;
710
use super::tag_resolver::find_tags_for_file;
811
use super::types::{CacheEncoding, CodeownersCache, CodeownersEntry, FileEntry};
12+
use crate::core::owner_resolver::find_owners_and_tags_for_file;
913
use crate::utils::error::{Error, Result};
1014

1115
/// Create a cache from parsed CODEOWNERS entries and files
1216
pub fn build_cache(
1317
entries: Vec<CodeownersEntry>, files: Vec<PathBuf>, hash: [u8; 32],
1418
) -> Result<CodeownersCache> {
15-
let mut file_entries = Vec::new();
1619
let mut owners_map = std::collections::HashMap::new();
1720
let mut tags_map = std::collections::HashMap::new();
1821

1922
println!("start building cache");
2023

2124
// Process each file to find owners and tags
22-
for file_path in files {
23-
let owners = find_owners_for_file(&file_path, &entries)?;
24-
let tags = find_tags_for_file(&file_path, &entries)?;
25-
26-
// Build file entry
27-
let file_entry = FileEntry {
28-
path: file_path.clone(),
29-
owners: owners.clone(),
30-
tags: tags.clone(),
31-
};
32-
dbg!(&file_entry);
33-
file_entries.push(file_entry);
34-
}
25+
let file_entries: Vec<FileEntry> = files
26+
.par_chunks(10)
27+
.flat_map(|chunk| {
28+
chunk
29+
.iter()
30+
.map(|file_path| {
31+
println!("Processing file: {}", file_path.display());
32+
33+
let (owners, tags) =
34+
find_owners_and_tags_for_file(file_path, &entries).unwrap();
35+
//let tags = find_tags_for_file(file_path, &entries).unwrap();
36+
37+
// Build file entry
38+
FileEntry {
39+
path: file_path.clone(),
40+
owners: owners.clone(),
41+
tags: tags.clone(),
42+
}
43+
})
44+
.collect::<Vec<FileEntry>>()
45+
})
46+
.collect();
3547

3648
println!("file entry done");
3749

src/core/owner_resolver.rs

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::smart_iter::SmartIter;
1+
use super::{smart_iter::SmartIter, types::Tag};
22
use crate::utils::error::{Error, Result};
33
use ignore::overrides::{Override, OverrideBuilder};
44
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
@@ -32,8 +32,8 @@ pub fn find_owners_for_file<'a>(
3232
.ok_or_else(|| Error::new("file path has no parent directory"))?;
3333

3434
// CodeownersEntry candidates
35-
let mut candidates = entries
36-
.smart_iter(3)
35+
let mut candidates: Vec<_> = entries
36+
.iter()
3737
.filter_map(|entry| {
3838
let codeowners_dir = match entry.source_file.parent() {
3939
Some(dir) => dir,
@@ -402,3 +402,92 @@ mod tests {
402402
assert_eq!(result[0].identifier, "@rust-team");
403403
}
404404
}
405+
406+
/// Find both owners and tags for a specific file based on all parsed CODEOWNERS entries
407+
pub fn find_owners_and_tags_for_file(
408+
file_path: &Path, entries: &[CodeownersEntry],
409+
) -> Result<(Vec<Owner>, Vec<Tag>)> {
410+
let target_dir = file_path.parent().ok_or_else(|| {
411+
std::io::Error::new(
412+
std::io::ErrorKind::InvalidInput,
413+
"file path has no parent directory",
414+
)
415+
})?;
416+
417+
let mut candidates: Vec<_> = entries
418+
.iter()
419+
.filter_map(|entry| {
420+
let codeowners_dir = match entry.source_file.parent() {
421+
Some(dir) => dir,
422+
None => {
423+
eprintln!(
424+
"CODEOWNERS entry has no parent directory: {}",
425+
entry.source_file.display()
426+
);
427+
return None;
428+
}
429+
};
430+
431+
// Check if the CODEOWNERS directory is an ancestor of the target directory
432+
if !target_dir.starts_with(codeowners_dir) {
433+
return None;
434+
}
435+
436+
// Calculate the depth as the number of components in the relative path from codeowners_dir to target_dir
437+
let rel_path = match target_dir.strip_prefix(codeowners_dir) {
438+
Ok(p) => p,
439+
Err(_) => return None, // Should not happen due to starts_with check
440+
};
441+
let depth = rel_path.components().count();
442+
443+
// Check if the pattern matches the target file
444+
let matches = {
445+
let mut builder = OverrideBuilder::new(codeowners_dir);
446+
if let Err(e) = builder.add(&entry.pattern) {
447+
eprintln!(
448+
"Invalid pattern '{}' in {}: {}",
449+
entry.pattern,
450+
entry.source_file.display(),
451+
e
452+
);
453+
return None;
454+
}
455+
let over: Override = match builder.build() {
456+
Ok(o) => o,
457+
Err(e) => {
458+
eprintln!(
459+
"Failed to build override for pattern '{}': {}",
460+
entry.pattern, e
461+
);
462+
return None;
463+
}
464+
};
465+
over.matched(file_path, false).is_whitelist()
466+
};
467+
468+
if matches { Some((entry, depth)) } else { None }
469+
})
470+
.collect();
471+
472+
// Sort the candidates by depth, source file, and line number
473+
candidates.sort_unstable_by(|a, b| {
474+
let a_entry = a.0;
475+
let a_depth = a.1;
476+
let b_entry = b.0;
477+
let b_depth = b.1;
478+
479+
// Primary sort by depth (ascending)
480+
a_depth
481+
.cmp(&b_depth)
482+
// Then by source file (to group entries from the same CODEOWNERS file)
483+
.then_with(|| a_entry.source_file.cmp(&b_entry.source_file))
484+
// Then by line number (descending) to prioritize later entries in the same file
485+
.then_with(|| b_entry.line_number.cmp(&a_entry.line_number))
486+
});
487+
488+
// Extract both owners and tags from the highest priority entry, if any
489+
Ok(candidates
490+
.first()
491+
.map(|(entry, _)| (entry.owners.clone(), entry.tags.clone()))
492+
.unwrap_or_default())
493+
}

src/core/tag_resolver.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ pub fn find_tags_for_file(file_path: &Path, entries: &[CodeownersEntry]) -> Resu
3131
)
3232
})?;
3333

34-
let mut candidates = entries
35-
.smart_iter(3)
34+
let mut candidates: Vec<_> = entries
35+
.iter()
3636
.filter_map(|entry| {
3737
let codeowners_dir = match entry.source_file.parent() {
3838
Some(dir) => dir,

0 commit comments

Comments
 (0)