1use std::collections::{BTreeMap, BTreeSet};
4
5#[derive(Debug, Clone, Default)]
7pub struct Coverage {
8 prev: BTreeMap<String, u64>,
9 toggled: BTreeSet<String>,
10 seen: BTreeSet<String>,
11}
12
13impl Coverage {
14 pub fn sample(&mut self, name: impl Into<String>, value: u64) {
15 let name = name.into();
16 self.seen.insert(name.clone());
17 if let Some(old) = self.prev.get(&name)
18 && *old != value
19 {
20 self.toggled.insert(name.clone());
21 }
22 self.prev.insert(name, value);
23 }
24
25 pub fn hits(&self) -> impl Iterator<Item = &str> {
26 self.toggled.iter().map(|s| s.as_str())
27 }
28
29 pub fn misses(&self) -> impl Iterator<Item = &str> {
30 self.seen.difference(&self.toggled).map(|s| s.as_str())
31 }
32
33 pub fn report(&self) -> String {
35 let mut out = String::from("# bitloom-sim coverage v1\n");
36 for n in self.hits() {
37 out.push_str(&format!("hit {n}\n"));
38 }
39 for n in self.misses() {
40 out.push_str(&format!("miss {n}\n"));
41 }
42 out
43 }
44}
45
46pub fn parse_report(text: &str) -> (Vec<String>, Vec<String>) {
47 let mut hits = Vec::new();
48 let mut misses = Vec::new();
49 for line in text.lines() {
50 if let Some(n) = line.strip_prefix("hit ") {
51 hits.push(n.to_string());
52 } else if let Some(n) = line.strip_prefix("miss ") {
53 misses.push(n.to_string());
54 }
55 }
56 (hits, misses)
57}