use super::{Merge, MergeError, Record};
use std::collections::BTreeMap;
use std::iter;
pub type Functions = BTreeMap<Key, Value>;
#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct Key {
pub name: String,
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct Value {
pub start_line: Option<u32>,
pub count: u64,
}
impl Merge for Value {
fn merge(&mut self, other: Self) -> Result<(), MergeError> {
if let Some(start_line) = other.start_line.as_ref() {
if let Some(my_start_line) = self.start_line.as_ref() {
if start_line != my_start_line {
return Err(MergeError::UnmatchedFunctionLine);
}
}
}
self.merge_lossy(other);
Ok(())
}
fn merge_lossy(&mut self, other: Self) {
if other.start_line.is_some() {
self.start_line = other.start_line;
}
self.count = u64::saturating_add(self.count, other.count);
}
}
pub(crate) fn into_records(functions: Functions) -> Box<dyn Iterator<Item = Record>> {
if functions.is_empty() {
return Box::new(iter::empty());
}
let found = functions.len() as u32;
let mut functions = functions.into_iter().collect::<Vec<_>>();
functions.sort_by_key(|(_, data)| data.start_line);
enum Func {
Line(String, u32),
Data(String, u64),
Found,
Hit(u32),
}
let line = functions.clone().into_iter().filter_map(|(key, data)| {
data.start_line
.map(|start_line| Func::Line(key.name, start_line))
});
let count = functions
.into_iter()
.map(|(key, data)| Func::Data(key.name, data.count));
let iter = line
.chain(count)
.chain(iter::once(Func::Found))
.chain(iter::once(Func::Hit(0)))
.scan(0, |hit_count, mut rec| {
match rec {
Func::Data(_, ref mut count) if *count > 0 => *hit_count += 1,
Func::Hit(ref mut hit) => *hit = *hit_count,
Func::Line(..) | Func::Data(..) | Func::Found => {}
}
Some(rec)
})
.map(move |rec| match rec {
Func::Line(name, start_line) => Record::FunctionName { name, start_line },
Func::Data(name, count) => Record::FunctionData { name, count },
Func::Found => Record::FunctionsFound { found },
Func::Hit(hit) => Record::FunctionsHit { hit },
});
Box::new(iter)
}