forge/manifest_ledger/
ranking.rs1use std::collections::{BTreeMap, BTreeSet};
5
6use super::types::LedgerRow;
7
8pub(super) fn total_by(
9 rows: &[LedgerRow],
10 key: impl Fn(&LedgerRow) -> &'static str,
11) -> BTreeMap<String, usize> {
12 let mut totals = BTreeMap::new();
13
14 for row in rows {
15 *totals.entry(key(row).to_string()).or_default() += 1;
16 }
17
18 totals
19}
20
21type LedgerGroupKey = (u8, String, Vec<Box<str>>, String);
22type LedgerGroupStats = (BTreeSet<String>, usize);
23
24pub(super) fn rank_and_sort(rows: &mut [LedgerRow]) {
25 let mut groups: BTreeMap<LedgerGroupKey, LedgerGroupStats> = BTreeMap::new();
26
27 for row in rows.iter() {
28 let key = (
29 row.disposition.sort_rank(),
30 row.category.as_str().to_string(),
31 row.semantic_ids.clone(),
32 row.evidence.clone(),
33 );
34 let group = groups.entry(key).or_default();
35
36 group.0.insert(row.spec_or_item.clone());
37 group.1 += 1;
38 }
39
40 for row in rows.iter_mut() {
41 let key = (
42 row.disposition.sort_rank(),
43 row.category.as_str().to_string(),
44 row.semantic_ids.clone(),
45 row.evidence.clone(),
46 );
47
48 if let Some((specs, operations)) = groups.get(&key) {
49 row.spec_count = specs.len();
50 row.operation_count = *operations;
51 }
52 }
53
54 rows.sort_by(|left, right| {
55 left.disposition
56 .sort_rank()
57 .cmp(&right.disposition.sort_rank())
58 .then(right.spec_count.cmp(&left.spec_count))
59 .then(right.operation_count.cmp(&left.operation_count))
60 .then(left.semantic_ids.cmp(&right.semantic_ids))
61 .then(left.manifest_path.cmp(&right.manifest_path))
62 .then(left.manifest_key.cmp(&right.manifest_key))
63 .then(left.spec_or_item.cmp(&right.spec_or_item))
64 });
65}