Skip to content

Commit f4ac229

Browse files
committed
wip: tag resolver optimizations
1 parent 8c65dba commit f4ac229

File tree

2 files changed

+99
-51
lines changed

2 files changed

+99
-51
lines changed

src/benches/tag_resolver_bench.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,45 @@ fn bench_find_files_for_tag_large_dataset(c: &mut Criterion) {
9898
});
9999
}
100100

101+
fn bench_find_files_for_tag_mega_large_dataset(c: &mut Criterion) {
102+
let target_tag = create_test_tag("core");
103+
let mut files = Vec::new();
104+
105+
// Create 25,000 files with mixed tags
106+
for i in 0..25000 {
107+
let tags = if i % 100 == 0 {
108+
// 1% of files with target tag
109+
vec![target_tag.clone()]
110+
} else if i % 100 == 1 {
111+
// 1% of files with target + another tag
112+
vec![target_tag.clone(), create_test_tag("shared")]
113+
} else if i % 100 == 2 {
114+
// 1% of files with target + multiple tags
115+
vec![
116+
target_tag.clone(),
117+
create_test_tag(&format!("module-{}", i % 20)),
118+
create_test_tag(&format!("type-{}", i % 10)),
119+
]
120+
} else {
121+
// 97% of files with other tags
122+
vec![create_test_tag(&format!("module-{}", i % 50))]
123+
};
124+
files.push(create_test_file_entry(
125+
&format!(
126+
"src/module_{}/submodule_{}/file_{}.rs",
127+
i / 1000,
128+
(i / 100) % 10,
129+
i
130+
),
131+
tags,
132+
));
133+
}
134+
135+
c.bench_function("find_files_for_tag_mega_large", |b| {
136+
b.iter(|| find_files_for_tag(black_box(&files), black_box(&target_tag)))
137+
});
138+
}
139+
101140
fn bench_find_files_for_tag_no_matches(c: &mut Criterion) {
102141
let target_tag = create_test_tag("nonexistent");
103142
let mut files = Vec::new();
@@ -402,6 +441,7 @@ criterion_group!(
402441
bench_find_files_for_tag_small_dataset,
403442
bench_find_files_for_tag_medium_dataset,
404443
bench_find_files_for_tag_large_dataset,
444+
bench_find_files_for_tag_mega_large_dataset,
405445
bench_find_files_for_tag_no_matches,
406446
bench_find_files_for_tag_multiple_tags_per_file,
407447
bench_find_tags_for_file_simple_pattern,

src/core/tag_resolver.rs

Lines changed: 59 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
use crate::utils::error::{Error, Result};
22
use ignore::overrides::{Override, OverrideBuilder};
3+
34
use std::path::{Path, PathBuf};
45

5-
use super::types::{CodeownersEntry, FileEntry, Tag};
6+
use super::{
7+
smart_iter::SmartIter,
8+
types::{CodeownersEntry, FileEntry, Tag},
9+
};
610

711
/// Find all files tagged with a specific tag
812
pub fn find_files_for_tag(files: &[FileEntry], tag: &Tag) -> Vec<PathBuf> {
913
files
1014
.iter()
11-
.filter(|file_entry| file_entry.tags.contains(tag))
12-
.map(|file_entry| file_entry.path.clone())
15+
.filter_map(|file_entry| {
16+
if file_entry.tags.contains(tag) {
17+
Some(file_entry.path.clone())
18+
} else {
19+
None
20+
}
21+
})
1322
.collect()
1423
}
1524

@@ -22,64 +31,63 @@ pub fn find_tags_for_file(file_path: &Path, entries: &[CodeownersEntry]) -> Resu
2231
)
2332
})?;
2433

25-
let mut candidates = Vec::new();
26-
27-
for entry in entries {
28-
let codeowners_dir = match entry.source_file.parent() {
29-
Some(dir) => dir,
30-
None => {
31-
eprintln!(
32-
"CODEOWNERS entry has no parent directory: {}",
33-
entry.source_file.display()
34-
);
35-
continue;
34+
let mut candidates = entries
35+
.smart_iter(3)
36+
.filter_map(|entry| {
37+
let codeowners_dir = match entry.source_file.parent() {
38+
Some(dir) => dir,
39+
None => {
40+
eprintln!(
41+
"CODEOWNERS entry has no parent directory: {}",
42+
entry.source_file.display()
43+
);
44+
return None;
45+
}
46+
};
47+
48+
// Check if the CODEOWNERS directory is an ancestor of the target directory
49+
if !target_dir.starts_with(codeowners_dir) {
50+
return None;
3651
}
37-
};
3852

39-
// Check if the CODEOWNERS directory is an ancestor of the target directory
40-
if !target_dir.starts_with(codeowners_dir) {
41-
continue;
42-
}
53+
// Calculate the depth as the number of components in the relative path from codeowners_dir to target_dir
54+
let rel_path = match target_dir.strip_prefix(codeowners_dir) {
55+
Ok(p) => p,
56+
Err(_) => return None, // Should not happen due to starts_with check
57+
};
58+
let depth = rel_path.components().count();
4359

44-
// Calculate the depth as the number of components in the relative path from codeowners_dir to target_dir
45-
let rel_path = match target_dir.strip_prefix(codeowners_dir) {
46-
Ok(p) => p,
47-
Err(_) => continue, // Should not happen due to starts_with check
48-
};
49-
let depth = rel_path.components().count();
50-
51-
// Check if the pattern matches the target file
52-
let matches = {
53-
let mut builder = OverrideBuilder::new(codeowners_dir);
54-
if let Err(e) = builder.add(&entry.pattern) {
55-
eprintln!(
56-
"Invalid pattern '{}' in {}: {}",
57-
entry.pattern,
58-
entry.source_file.display(),
59-
e
60-
);
61-
continue;
62-
}
63-
let over: Override = match builder.build() {
64-
Ok(o) => o,
65-
Err(e) => {
60+
// Check if the pattern matches the target file
61+
let matches = {
62+
let mut builder = OverrideBuilder::new(codeowners_dir);
63+
if let Err(e) = builder.add(&entry.pattern) {
6664
eprintln!(
67-
"Failed to build override for pattern '{}': {}",
68-
entry.pattern, e
65+
"Invalid pattern '{}' in {}: {}",
66+
entry.pattern,
67+
entry.source_file.display(),
68+
e
6969
);
70-
continue;
70+
return None;
7171
}
72+
let over: Override = match builder.build() {
73+
Ok(o) => o,
74+
Err(e) => {
75+
eprintln!(
76+
"Failed to build override for pattern '{}': {}",
77+
entry.pattern, e
78+
);
79+
return None;
80+
}
81+
};
82+
over.matched(file_path, false).is_whitelist()
7283
};
73-
over.matched(file_path, false).is_whitelist()
74-
};
7584

76-
if matches {
77-
candidates.push((entry, depth));
78-
}
79-
}
85+
if matches { Some((entry, depth)) } else { None }
86+
})
87+
.collect();
8088

8189
// Sort the candidates by depth, source file, and line number
82-
candidates.sort_by(|a, b| {
90+
candidates.sort_unstable_by(|a, b| {
8391
let a_entry = a.0;
8492
let a_depth = a.1;
8593
let b_entry = b.0;

0 commit comments

Comments
 (0)