1use std::fs;
2use std::io::{BufRead, BufReader, BufWriter, Write};
3use std::path::{Path, PathBuf};
4
5use crate::error::*;
6
7#[derive(Clone, Debug, PartialEq)]
9pub struct LineCoverage {
10 pub line_number: usize,
12 pub count: Option<u32>,
15}
16
17#[derive(Clone, Debug, PartialEq)]
19pub struct BranchCoverage {
20 pub line_number: usize,
22 pub block_number: Option<usize>,
24 pub taken: Option<bool>,
27}
28
29#[derive(Debug, PartialEq)]
33pub struct FileCoverage {
34 path: PathBuf,
35 #[doc(hidden)]
36 pub line_coverages: Vec<LineCoverage>,
37 #[doc(hidden)]
38 pub branch_coverages: Vec<BranchCoverage>,
39}
40
41impl FileCoverage {
42 pub fn new<P: Into<PathBuf>>(
43 path: P,
44 line_coverages: Vec<LineCoverage>,
45 branch_coverages: Vec<BranchCoverage>,
46 ) -> Self {
47 Self {
48 path: path.into(),
49 line_coverages,
50 branch_coverages,
51 }
52 }
53
54 pub fn path(&self) -> &Path {
55 &self.path
56 }
57
58 pub fn line_coverages(&self) -> &[LineCoverage] {
59 &self.line_coverages
60 }
61
62 pub fn branch_coverages(&self) -> &[BranchCoverage] {
63 &self.branch_coverages
64 }
65}
66
67#[derive(Debug, PartialEq)]
69pub struct PackageCoverage {
70 name: String,
71 #[doc(hidden)]
72 pub file_coverages: Vec<FileCoverage>,
73}
74
75impl PackageCoverage {
76 pub fn new(file_coverages: Vec<FileCoverage>) -> Self {
77 Self::with_test_name("", file_coverages)
78 }
79
80 pub fn with_test_name<T: Into<String>>(name: T, file_coverages: Vec<FileCoverage>) -> Self {
81 Self {
82 name: name.into(),
83 file_coverages,
84 }
85 }
86
87 pub fn name(&self) -> &str {
88 &self.name
89 }
90
91 pub fn file_coverages(&self) -> &[FileCoverage] {
92 &self.file_coverages
93 }
94}
95
96#[doc(hidden)]
97pub trait TotalCoverage {
98 fn line_executed(&self) -> usize;
99 fn line_total(&self) -> usize;
100 fn branch_executed(&self) -> usize;
101 fn branch_total(&self) -> usize;
102}
103
104#[doc(hidden)]
105impl TotalCoverage for FileCoverage {
106 fn line_executed(&self) -> usize {
107 self.line_coverages
108 .iter()
109 .filter(|&v| v.count.map_or(false, |c| c > 0))
110 .count()
111 }
112
113 fn line_total(&self) -> usize {
114 self.line_coverages
115 .iter()
116 .filter(|&v| v.count.is_some())
117 .count()
118 }
119
120 fn branch_executed(&self) -> usize {
121 self.branch_coverages
122 .iter()
123 .filter(|&v| v.taken.unwrap_or(false))
124 .count()
125 }
126
127 fn branch_total(&self) -> usize {
128 self.branch_coverages
129 .iter()
130 .filter(|&v| v.taken.is_some())
131 .count()
132 }
133}
134
135#[doc(hidden)]
136impl TotalCoverage for PackageCoverage {
137 fn line_executed(&self) -> usize {
138 self.file_coverages
139 .iter()
140 .fold(0, |sum, a| sum + a.line_executed())
141 }
142
143 fn line_total(&self) -> usize {
144 self.file_coverages
145 .iter()
146 .fold(0, |sum, a| sum + a.line_total())
147 }
148
149 fn branch_executed(&self) -> usize {
150 self.file_coverages
151 .iter()
152 .fold(0, |sum, a| sum + a.branch_executed())
153 }
154
155 fn branch_total(&self) -> usize {
156 self.file_coverages
157 .iter()
158 .fold(0, |sum, a| sum + a.branch_total())
159 }
160}
161
162pub trait CoverageReader {
163 fn read<R: BufRead>(&self, reader: &mut R) -> Result<PackageCoverage, Error>;
165
166 fn read_from_file(&self, path: &Path) -> Result<PackageCoverage, Error> {
168 let f = fs::File::open(path)
169 .chain_err(|| format!("Failed to open coverage file {:?}", path))?;
170 let capacity = f.metadata().map(|m| m.len() as usize + 1).unwrap_or(8192);
171 let mut reader = BufReader::with_capacity(capacity, f);
172 self.read(&mut reader)
173 }
174}
175
176pub trait CoverageWriter {
177 fn write<W: Write>(&self, data: &PackageCoverage, writer: &mut W) -> Result<(), Error>;
179
180 fn write_to_file(&self, data: &PackageCoverage, path: &Path) -> Result<(), Error> {
182 let f = fs::File::create(path).chain_err(|| format!("Failed to open file {:?}", path))?;
183 let mut writer = BufWriter::new(f);
184 self.write(&data, &mut writer)
185 }
186}