qlty_coverage/
transformer.rs1use anyhow::Result;
2use globset::{Glob, GlobSet, GlobSetBuilder};
3use qlty_analysis::utils::fs::path_to_string;
4use qlty_types::tests::v1::{CoverageMetadata, CoverageSummary, FileCoverage};
5use std::{
6 env::current_dir,
7 fmt::Debug,
8 path::{PathBuf, MAIN_SEPARATOR},
9};
10
11pub trait Transformer: Debug + Send + Sync + 'static {
12 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage>;
13 fn clone_box(&self) -> Box<dyn Transformer>;
14}
15
16impl Clone for Box<dyn Transformer> {
17 fn clone(&self) -> Self {
18 self.clone_box()
19 }
20}
21
22#[derive(Debug, Clone)]
23pub struct ComputeSummary {}
24
25impl ComputeSummary {
26 pub fn new() -> Self {
27 Self {}
28 }
29}
30
31impl Transformer for ComputeSummary {
32 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage> {
33 let mut covered = 0;
34 let mut missed = 0;
35 let mut omit = 0;
36
37 for hit in &file_coverage.hits {
38 match hit {
39 -1 => omit += 1,
40 0 => missed += 1,
41 _ => covered += 1,
42 }
43 }
44
45 let mut file_coverage = file_coverage;
46
47 file_coverage.summary = Some(CoverageSummary {
48 covered,
49 missed,
50 omit,
51 total: covered + missed + omit,
52 });
53
54 Some(file_coverage)
55 }
56
57 fn clone_box(&self) -> Box<dyn Transformer> {
58 Box::new(self.clone())
59 }
60}
61
62#[derive(Debug, Clone)]
63pub struct AppendMetadata {
64 metadata: CoverageMetadata,
65}
66
67impl AppendMetadata {
68 pub fn new(metadata: &CoverageMetadata) -> Self {
69 Self {
70 metadata: metadata.clone(),
71 }
72 }
73}
74
75impl Transformer for AppendMetadata {
76 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage> {
77 let mut file_coverage = file_coverage;
78 file_coverage.build_id = self.metadata.build_id.clone();
79 file_coverage.tag = self.metadata.tag.clone();
80 file_coverage.branch = self.metadata.branch.clone();
81 file_coverage.commit_sha = Some(self.metadata.commit_sha.clone());
82 file_coverage.uploaded_at = self.metadata.uploaded_at.clone();
83
84 if self.metadata.pull_request_number != String::default() {
85 file_coverage.pull_request_number = Some(self.metadata.pull_request_number.clone());
86 }
87
88 Some(file_coverage)
89 }
90
91 fn clone_box(&self) -> Box<dyn Transformer> {
92 Box::new(self.clone())
93 }
94}
95
96#[derive(Debug, Clone)]
97pub struct IgnorePaths {
98 glob_set: GlobSet,
99}
100
101impl IgnorePaths {
102 pub fn new(paths: &[String]) -> Result<Self> {
103 let mut builder = GlobSetBuilder::new();
104
105 for glob in paths {
106 builder.add(Glob::new(&glob)?);
107 }
108
109 Ok(Self {
110 glob_set: builder.build()?,
111 })
112 }
113}
114
115impl Transformer for IgnorePaths {
116 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage> {
117 if self.glob_set.is_match(&file_coverage.path) {
118 None
119 } else {
120 Some(file_coverage)
121 }
122 }
123
124 fn clone_box(&self) -> Box<dyn Transformer> {
125 Box::new(self.clone())
126 }
127}
128
129#[derive(Debug, Clone)]
130pub struct AddPrefix {
131 prefix: String,
132}
133
134impl AddPrefix {
135 pub fn new(prefix: &str) -> Self {
136 Self {
137 prefix: prefix.to_owned(),
138 }
139 }
140}
141
142impl Transformer for AddPrefix {
143 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage> {
144 let mut file_coverage = file_coverage;
145 file_coverage.path = format!("{}{}", self.prefix, file_coverage.path);
146 Some(file_coverage)
147 }
148
149 fn clone_box(&self) -> Box<dyn Transformer> {
150 Box::new(self.clone())
151 }
152}
153
154#[derive(Debug, Clone)]
155pub struct StripPrefix {
156 prefix: String,
157}
158
159impl Default for StripPrefix {
160 fn default() -> Self {
161 Self {
162 prefix: format!(
163 "{}{}",
164 path_to_string(current_dir().unwrap_or_else(|_| PathBuf::from("."))),
165 MAIN_SEPARATOR
166 ),
167 }
168 }
169}
170
171impl StripPrefix {
172 pub fn new(prefix: String) -> Self {
173 Self { prefix: prefix }
174 }
175}
176
177impl Transformer for StripPrefix {
178 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage> {
179 let mut file_coverage = file_coverage;
180 file_coverage.path = file_coverage.path.replacen(&self.prefix, "", 1);
181 Some(file_coverage)
182 }
183
184 fn clone_box(&self) -> Box<dyn Transformer> {
185 Box::new(self.clone())
186 }
187}
188
189#[derive(Debug, Clone)]
190pub struct StripDotSlashPrefix;
191
192impl Transformer for StripDotSlashPrefix {
193 fn transform(&self, file_coverage: FileCoverage) -> Option<FileCoverage> {
194 let mut file_coverage = file_coverage;
195 if file_coverage.path.starts_with("./") {
196 file_coverage.path = file_coverage.path[2..].to_string();
197 }
198 Some(file_coverage)
199 }
200
201 fn clone_box(&self) -> Box<dyn Transformer> {
202 Box::new(Self)
203 }
204}