Skip to main content

crap4rust/
coverage_index.rs

1// Copyright 2025 Umberto Gotti <umberto.gotti@umbertogotti.dev>
2// Licensed under the MIT License or Apache License, Version 2.0
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use std::collections::HashMap;
6
7use crate::model::{CoverageRecord, SourceFunction};
8
9pub struct CoverageIndex {
10    inner: HashMap<(String, usize), CoverageRecord>,
11}
12
13impl CoverageIndex {
14    pub fn from_records(records: Vec<CoverageRecord>) -> Self {
15        let mut inner = HashMap::new();
16        for record in records {
17            let key = (record.path_key.clone(), record.line);
18            inner
19                .entry(key)
20                .and_modify(|existing: &mut CoverageRecord| {
21                    existing.covered_regions += record.covered_regions;
22                    existing.total_regions += record.total_regions;
23                })
24                .or_insert(record);
25        }
26        Self { inner }
27    }
28
29    pub fn match_function(&self, function: &SourceFunction) -> Option<f64> {
30        match_function_coverage(function, &self.inner).map(|record| record.coverage_ratio())
31    }
32}
33
34pub fn match_function_coverage(
35    function: &SourceFunction,
36    coverage_index: &HashMap<(String, usize), CoverageRecord>,
37) -> Option<CoverageRecord> {
38    let matching: Vec<&CoverageRecord> = coverage_index
39        .iter()
40        .filter(|((path_key, line), _)| {
41            path_key == &function.path_key && *line >= function.line && *line <= function.end_line
42        })
43        .map(|(_, record)| record)
44        .collect();
45
46    if matching.is_empty() {
47        return None;
48    }
49
50    let covered_regions = matching.iter().map(|r| r.covered_regions).sum();
51    let total_regions = matching.iter().map(|r| r.total_regions).sum();
52
53    Some(CoverageRecord {
54        path_key: function.path_key.clone(),
55        line: function.line,
56        covered_regions,
57        total_regions,
58    })
59}