Skip to content

Commit 610b96f

Browse files
committed
refactor: split eval runner matching helpers
Separate required-match allocation, unexpected-match detection, and rule-metric assembly so fixture expectation evaluation stays easy to follow. Made-with: Cursor
1 parent 9b134f4 commit 610b96f

5 files changed

Lines changed: 124 additions & 49 deletions

File tree

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
- [x] `src/commands/feedback_eval/report/build/stats.rs`: split threshold confusion-matrix scoring from bucket primitives.
7171
- [x] `src/commands/doctor/command/display.rs`: separate header/config output, endpoint listing, and inference result rendering.
7272
- [x] `src/commands/doctor/command/run.rs`: separate endpoint discovery, recommendation flow, and test helpers.
73-
- [ ] `src/commands/eval/runner/matching.rs`: split required-match search, unexpected-match detection, and rule metric assembly.
73+
- [x] `src/commands/eval/runner/matching.rs`: split required-match search, unexpected-match detection, and rule metric assembly.
7474
- [ ] `src/commands/eval/runner/execute/loading.rs`: separate diff resolution from repo-path resolution if it grows again.
7575
- [ ] `src/commands/feedback_eval/report/examples.rs`: split ranking helpers from example builders.
7676
- [ ] `src/commands/doctor/system.rs`: carve environment probes vs output helpers.
Lines changed: 22 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
#[path = "matching/required.rs"]
2+
mod required;
3+
#[path = "matching/rules.rs"]
4+
mod rules;
5+
#[path = "matching/unexpected.rs"]
6+
mod unexpected;
7+
18
use std::collections::HashSet;
29

310
use crate::core;
411

5-
use super::super::metrics::{compute_rule_metrics, summarize_rule_metrics};
6-
use super::super::pattern::summarize_for_eval;
712
use super::super::{EvalExpectations, EvalRuleMetrics, EvalRuleScoreSummary};
13+
use required::collect_required_matches;
14+
use rules::build_rule_match_metrics;
15+
use unexpected::collect_unexpected_matches;
816

917
pub(super) struct FixtureMatchSummary {
1018
pub(super) failures: Vec<String>,
@@ -20,57 +28,23 @@ pub(super) fn evaluate_fixture_expectations(
2028
expectations: &EvalExpectations,
2129
comments: &[core::Comment],
2230
) -> FixtureMatchSummary {
23-
let mut failures = Vec::new();
24-
let mut required_matches = 0usize;
2531
let required_total = expectations.must_find.len();
26-
let mut used_comment_indices = HashSet::new();
27-
let mut unexpected_comment_indices = HashSet::new();
28-
let mut matched_pairs = Vec::new();
29-
30-
for (expected_idx, expected) in expectations.must_find.iter().enumerate() {
31-
let found = comments
32-
.iter()
33-
.enumerate()
34-
.find(|(comment_idx, comment)| {
35-
!used_comment_indices.contains(comment_idx) && expected.matches(comment)
36-
})
37-
.map(|(comment_idx, _)| comment_idx);
38-
39-
if let Some(comment_idx) = found {
40-
used_comment_indices.insert(comment_idx);
41-
matched_pairs.push((expected_idx, comment_idx));
42-
required_matches = required_matches.saturating_add(1);
43-
} else {
44-
failures.push(format!("Missing expected finding: {}", expected.describe()));
45-
}
46-
}
47-
48-
for unexpected in &expectations.must_not_find {
49-
if let Some((comment_idx, comment)) = comments
50-
.iter()
51-
.enumerate()
52-
.find(|(_, comment)| unexpected.matches(comment))
53-
{
54-
unexpected_comment_indices.insert(comment_idx);
55-
failures.push(format!(
56-
"Unexpected finding matched {}:{} '{}'",
57-
comment.file_path.display(),
58-
comment.line_number,
59-
summarize_for_eval(&comment.content)
60-
));
61-
}
62-
}
63-
64-
let rule_metrics = compute_rule_metrics(&expectations.must_find, comments, &matched_pairs);
65-
let rule_summary = summarize_rule_metrics(&rule_metrics);
32+
let required = collect_required_matches(expectations, comments);
33+
let unexpected = collect_unexpected_matches(expectations, comments);
34+
let (rule_metrics, rule_summary) =
35+
build_rule_match_metrics(expectations, comments, &required.matched_pairs);
6636

6737
FixtureMatchSummary {
68-
failures,
69-
required_matches,
38+
failures: required
39+
.failures
40+
.into_iter()
41+
.chain(unexpected.failures)
42+
.collect(),
43+
required_matches: required.required_matches,
7044
required_total,
7145
rule_metrics,
7246
rule_summary,
73-
used_comment_indices,
74-
unexpected_comment_indices,
47+
used_comment_indices: required.used_comment_indices,
48+
unexpected_comment_indices: unexpected.unexpected_comment_indices,
7549
}
7650
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::collections::HashSet;
2+
3+
use crate::core;
4+
5+
use super::super::super::EvalExpectations;
6+
7+
pub(super) struct RequiredMatchResults {
8+
pub(super) failures: Vec<String>,
9+
pub(super) required_matches: usize,
10+
pub(super) used_comment_indices: HashSet<usize>,
11+
pub(super) matched_pairs: Vec<(usize, usize)>,
12+
}
13+
14+
pub(super) fn collect_required_matches(
15+
expectations: &EvalExpectations,
16+
comments: &[core::Comment],
17+
) -> RequiredMatchResults {
18+
let mut failures = Vec::new();
19+
let mut required_matches = 0usize;
20+
let mut used_comment_indices = HashSet::new();
21+
let mut matched_pairs = Vec::new();
22+
23+
for (expected_idx, expected) in expectations.must_find.iter().enumerate() {
24+
let found = comments
25+
.iter()
26+
.enumerate()
27+
.find(|(comment_idx, comment)| {
28+
!used_comment_indices.contains(comment_idx) && expected.matches(comment)
29+
})
30+
.map(|(comment_idx, _)| comment_idx);
31+
32+
if let Some(comment_idx) = found {
33+
used_comment_indices.insert(comment_idx);
34+
matched_pairs.push((expected_idx, comment_idx));
35+
required_matches = required_matches.saturating_add(1);
36+
} else {
37+
failures.push(format!("Missing expected finding: {}", expected.describe()));
38+
}
39+
}
40+
41+
RequiredMatchResults {
42+
failures,
43+
required_matches,
44+
used_comment_indices,
45+
matched_pairs,
46+
}
47+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use crate::core;
2+
3+
use super::super::super::metrics::{compute_rule_metrics, summarize_rule_metrics};
4+
use super::super::super::{EvalExpectations, EvalRuleMetrics, EvalRuleScoreSummary};
5+
6+
pub(super) fn build_rule_match_metrics(
7+
expectations: &EvalExpectations,
8+
comments: &[core::Comment],
9+
matched_pairs: &[(usize, usize)],
10+
) -> (Vec<EvalRuleMetrics>, Option<EvalRuleScoreSummary>) {
11+
let rule_metrics = compute_rule_metrics(&expectations.must_find, comments, matched_pairs);
12+
let rule_summary = summarize_rule_metrics(&rule_metrics);
13+
(rule_metrics, rule_summary)
14+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::collections::HashSet;
2+
3+
use crate::core;
4+
5+
use super::super::super::pattern::summarize_for_eval;
6+
use super::super::super::EvalExpectations;
7+
8+
pub(super) struct UnexpectedMatchResults {
9+
pub(super) failures: Vec<String>,
10+
pub(super) unexpected_comment_indices: HashSet<usize>,
11+
}
12+
13+
pub(super) fn collect_unexpected_matches(
14+
expectations: &EvalExpectations,
15+
comments: &[core::Comment],
16+
) -> UnexpectedMatchResults {
17+
let mut failures = Vec::new();
18+
let mut unexpected_comment_indices = HashSet::new();
19+
20+
for unexpected in &expectations.must_not_find {
21+
if let Some((comment_idx, comment)) = comments
22+
.iter()
23+
.enumerate()
24+
.find(|(_, comment)| unexpected.matches(comment))
25+
{
26+
unexpected_comment_indices.insert(comment_idx);
27+
failures.push(format!(
28+
"Unexpected finding matched {}:{} '{}'",
29+
comment.file_path.display(),
30+
comment.line_number,
31+
summarize_for_eval(&comment.content)
32+
));
33+
}
34+
}
35+
36+
UnexpectedMatchResults {
37+
failures,
38+
unexpected_comment_indices,
39+
}
40+
}

0 commit comments

Comments
 (0)