in src/parser.rs [733:823]
fn parse_jacoco_report_package<T: BufRead>(
parser: &mut Reader<T>,
buf: &mut Vec<u8>,
package: &str,
) -> Result<Vec<(String, CovResult)>, ParserError> {
let mut results_map: FxHashMap<String, CovResult> = FxHashMap::default();
loop {
match parser.read_event_into(buf) {
Ok(Event::Start(ref e)) => {
match e.local_name().into_inner() {
b"class" => {
let fq_class = get_xml_attribute(parser, e, "name")?;
// Class name: "Person$Age"
let class = fq_class
.split('/')
.next_back()
.expect("Failed to parse class name");
// Class name "Person"
let top_class = class
.split('$')
.next()
.expect("Failed to parse top class name");
// Fully qualified class name: "org/example/Person$Age"
// Generally, we will use the filename if its present,
// but if it isn't, fallback to the top level class name
let file = get_xml_attribute(parser, e, "sourcefilename")
.unwrap_or(format!("{}.java", top_class));
// Process all <method /> and <counter /> for this class
let functions = parse_jacoco_report_class(parser, buf, class)?;
match results_map.entry(file.to_string()) {
hash_map::Entry::Occupied(obj) => {
obj.into_mut().functions.extend(functions);
}
hash_map::Entry::Vacant(v) => {
v.insert(CovResult {
functions,
lines: BTreeMap::new(),
branches: BTreeMap::new(),
});
}
};
}
b"sourcefile" => {
// Fully qualified class name: "org/example/Person$Age"
let file = get_xml_attribute(parser, e, "name")?;
let JacocoReport { lines, branches } =
parse_jacoco_report_sourcefile(parser, buf)?;
match results_map.entry(file.to_string()) {
hash_map::Entry::Occupied(obj) => {
let obj = obj.into_mut();
obj.lines = lines;
obj.branches = branches;
}
hash_map::Entry::Vacant(v) => {
v.insert(CovResult {
functions: FxHashMap::default(),
lines,
branches,
});
}
};
}
&_ => {}
}
}
Ok(Event::End(ref e)) if e.local_name().into_inner() == b"package" => break,
Err(e) => return Err(ParserError::Parse(e.to_string())),
_ => {}
}
}
// Change all keys from the class name to the file name and turn the result into a Vec.
// If package is the empty string, we have to trim the leading '/' in order to obtain a
// relative path.
Ok(results_map
.into_iter()
.map(|(class, result)| {
(
format!("{}/{}", package, class)
.trim_start_matches('/')
.to_string(),
result,
)
})
.collect())
}