lcov_parser/report/
line.rs1use std::io;
10use std::cmp::PartialEq;
11use std::collections::btree_map:: { BTreeMap };
12use std::convert::{ AsRef, From };
13use std::fmt:: { Display, Formatter, Result };
14use record:: { LineData, RecordWrite };
15use report::summary:: { Summary };
16use report::attribute:: { LineNumber, CheckSum, ExecutionCount };
17use report::counter:: { Hit, HitFoundCounter, FoundCounter, HitCounter };
18use merger::ops:: { TryMerge, MergeResult, MergeLine, ChecksumError };
19
20#[derive(Debug, Eq, Clone)]
21pub struct Line {
22 line_number: LineNumber,
23 execution_count: ExecutionCount,
24 checksum: Option<CheckSum>
25}
26
27impl Line {
28 pub fn new(
29 line_number: LineNumber,
30 execution_count: ExecutionCount,
31 checksum: Option<CheckSum>
32 ) -> Self {
33 Line {
34 line_number: line_number,
35 execution_count: execution_count,
36 checksum: checksum
37 }
38 }
39 pub fn line_number(&self) -> &LineNumber {
40 &self.line_number
41 }
42 pub fn execution_count(&self) -> &ExecutionCount {
43 &self.execution_count
44 }
45 pub fn checksum(&self) -> Option<&CheckSum> {
46 match self.checksum {
47 Some(ref v) => Some(v),
48 None => None
49 }
50 }
51 pub fn has_checkshum(&self) -> bool {
52 self.checksum.is_some()
53 }
54 pub fn is_hit(&self) -> bool {
55 self.execution_count.is_hit()
56 }
57}
58
59impl<'a> From<&'a LineData> for Line {
60 fn from(line_data: &'a LineData) -> Self {
61 Line::new(
62 line_data.line,
63 line_data.count,
64 line_data.checksum.clone()
65 )
66 }
67}
68
69impl PartialEq for Line {
70 fn eq(&self, other: &Self) -> bool {
71 let has_checkshum = self.has_checkshum() && other.has_checkshum();
72 if has_checkshum {
73 return self.checksum.as_ref() == other.checksum();
74 }
75 return &self.line_number == other.line_number();
76 }
77}
78
79impl<'a> TryMerge<&'a Line> for Line {
80 type Err = ChecksumError;
81
82 fn try_merge(&mut self, other: &'a Line) -> MergeResult<Self::Err> {
83 if let Some(o) = other.checksum() {
84 if let Some(ref s) = self.checksum {
85 if s != o {
86 return Err(ChecksumError::Mismatch(
87 MergeLine::from(&self.clone()),
88 MergeLine::from(other)
89 ));
90 }
91 }
92 self.checksum = Some(o.clone());
93 }
94 self.execution_count += *other.execution_count();
95 Ok(())
96 }
97}
98
99impl<'a> TryMerge<&'a LineData> for Line {
100 type Err = ChecksumError;
101
102 fn try_merge(&mut self, other: &'a LineData) -> MergeResult<Self::Err> {
103 self.try_merge(&Line::from(other))
104 }
105}
106
107
108#[derive(Debug, Clone)]
109pub struct Lines {
110 lines: BTreeMap<LineNumber, Line>
111}
112
113impl Lines {
114 pub fn new() -> Self {
115 Lines {
116 lines: BTreeMap::new()
117 }
118 }
119}
120
121impl AsRef<BTreeMap<LineNumber, Line>> for Lines {
122 fn as_ref(&self) -> &BTreeMap<LineNumber, Line> {
123 &self.lines
124 }
125}
126
127impl_summary!(Lines, lines<LineNumber, Line>);
128
129
130impl HitCounter for Lines {
131 fn hit_count(&self) -> usize {
132 self.iter()
133 .filter(|&(_, line)| line.is_hit() )
134 .count()
135 }
136}
137
138impl FoundCounter for Lines {
139 fn found_count(&self) -> usize {
140 self.lines.len()
141 }
142}
143
144impl HitFoundCounter for Lines {
145}
146
147
148
149impl RecordWrite for Lines {
150 fn write_records<T: io::Write>(&self, output: &mut T) -> io::Result<()> {
151 write!(output, "{}", self)
152 }
153}
154
155impl Display for Lines {
156 fn fmt(&self, f: &mut Formatter) -> Result {
157 if self.is_empty() {
158 return Ok(());
159 }
160 for (_, line) in self.iter() {
161 match line.checksum() {
162 Some(ref checksum) => writeln!(f, "DA:{},{},{}", line.line_number(), line.execution_count(), checksum)?,
163 None => writeln!(f, "DA:{},{}", line.line_number(), line.execution_count())?
164 }
165 }
166 writeln!(f, "LF:{}", self.found_count())?;
167 writeln!(f, "LH:{}", self.hit_count())?;
168 Ok(())
169 }
170}
171
172
173impl<'a> TryMerge<&'a LineData> for Lines {
174 type Err = ChecksumError;
175
176 fn try_merge(&mut self, line_data: &'a LineData) -> MergeResult<Self::Err> {
177 if !self.lines.contains_key(&line_data.line) {
178 self.lines.insert(line_data.line, Line::from(line_data));
179 return Ok(());
180 }
181 let line = self.lines.get_mut(&line_data.line).unwrap();
182 line.try_merge(line_data)
183 }
184}
185
186
187impl_try_merge_self_summary!(Lines:lines, ChecksumError);
188
189
190#[cfg(test)]
191mod tests {
192 use merger::ops::*;
193 use record:: { LineData };
194 use report::line:: { Line, Lines };
195 use report::summary:: { Summary };
196 use report::counter:: { FoundCounter, HitCounter };
197
198 #[test]
199 fn add_line_data() {
200 let mut lines = Lines::new();
201 lines.try_merge(&LineData { line: 1, count: 1, checksum: Some("abc".to_string()) }).unwrap();
202 lines.try_merge(&LineData { line: 1, count: 1, checksum: Some("abc".to_string()) }).unwrap();
203
204 let result = lines.clone();
205 assert_eq!( result.get(&1), Some(&Line::new(1, 2, Some("abc".to_string()))) );
206 }
207
208 #[test]
209 fn add_lines_data() {
210 let mut lines = Lines::new();
211 lines.try_merge(&LineData { line: 1, count: 1, checksum: Some("abc".to_string()) }).unwrap();
212
213 let ref cloned_lines = lines.clone();
214 lines.try_merge(cloned_lines).unwrap();
215
216 assert_eq!( lines.get(&1), Some(&Line::new(1, 2, Some("abc".to_string()))) );
217 }
218
219 #[test]
220 fn hit_count_and_found_count() {
221 let mut lines = Lines::new();
222
223 lines.try_merge(&LineData { line: 1, count: 1, checksum: Some("abc".to_string()) }).unwrap();
224 lines.try_merge(&LineData { line: 2, count: 0, checksum: Some("def".to_string()) }).unwrap();
225
226 assert_eq!( lines.hit_count(), 1 );
227 assert_eq!( lines.found_count(), 2 );
228 }
229}