qlty_coverage/publish/
planner.rs

1use crate::git::retrieve_commit_metadata;
2use crate::publish::Plan;
3use crate::publish::Settings;
4use crate::transformer::AddPrefix;
5use crate::transformer::AppendMetadata;
6use crate::transformer::ComputeSummary;
7use crate::transformer::IgnorePaths;
8use crate::transformer::StripDotSlashPrefix;
9use crate::transformer::StripPrefix;
10use crate::utils::extract_path_and_format;
11use crate::Transformer;
12use anyhow::Result;
13use pbjson_types::Timestamp;
14use qlty_config::QltyConfig;
15use qlty_types::tests::v1::CoverageMetadata;
16use qlty_types::tests::v1::ReportFile;
17use std::vec;
18use time::OffsetDateTime;
19
20#[derive(Debug, Clone)]
21pub struct Planner {
22    config: QltyConfig,
23    settings: Settings,
24}
25
26impl Planner {
27    pub fn new(config: &QltyConfig, settings: &Settings) -> Self {
28        Self {
29            config: config.clone(),
30            settings: settings.clone(),
31        }
32    }
33
34    pub fn compute(&self) -> Result<Plan> {
35        let metadata = self.compute_metadata()?;
36
37        Ok(Plan {
38            metadata: metadata.clone(),
39            report_files: self.compute_report_files()?,
40            transformers: self.compute_transformers(&metadata)?,
41        })
42    }
43
44    fn compute_metadata(&self) -> Result<CoverageMetadata> {
45        let now = OffsetDateTime::now_utc();
46
47        let mut metadata = if let Some(ci) = crate::ci::current() {
48            ci.metadata()
49        } else {
50            let mut metadata = CoverageMetadata::default();
51            metadata.ci = "unknown".to_string();
52            metadata
53        };
54
55        metadata.uploaded_at = Some(Timestamp {
56            seconds: now.unix_timestamp() as i64,
57            nanos: now.nanosecond() as i32,
58        });
59        metadata.tag = self.settings.tag.clone();
60
61        // Override metadata with command line arguments
62        if let Some(build_id) = self.settings.override_build_id.clone() {
63            metadata.build_id = build_id;
64        }
65
66        if let Some(commit_sha) = self.settings.override_commit_sha.clone() {
67            metadata.commit_sha = commit_sha;
68        }
69
70        if let Some(branch) = self.settings.override_branch.clone() {
71            metadata.branch = branch;
72        }
73
74        if let Some(pull_request_number) = self.settings.override_pull_request_number.clone() {
75            metadata.pull_request_number = pull_request_number;
76        }
77
78        let commit_metadata = retrieve_commit_metadata()?;
79        metadata.commit_message = commit_metadata.commit_message;
80        metadata.committer_email = commit_metadata.committer_email;
81        metadata.committer_name = commit_metadata.committer_name;
82        metadata.author_email = commit_metadata.author_email;
83        metadata.author_name = commit_metadata.author_name;
84        metadata.author_time = Some(Timestamp {
85            seconds: commit_metadata.author_time.seconds(),
86            nanos: 0,
87        });
88
89        metadata.commit_time = Some(Timestamp {
90            seconds: commit_metadata.commit_time.seconds(),
91            nanos: 0,
92        });
93
94        Ok(metadata)
95    }
96
97    fn compute_report_files(&self) -> Result<Vec<ReportFile>> {
98        let paths = if self.settings.paths.is_empty() {
99            self.config.coverage.paths.clone().unwrap_or_default()
100        } else {
101            self.settings.paths.clone()
102        };
103
104        let mut report_files: Vec<ReportFile> = vec![];
105
106        for path in paths {
107            let (path, format) =
108                extract_path_and_format(&path, self.settings.report_format.clone())?;
109
110            report_files.push(ReportFile {
111                path: path.to_string_lossy().into_owned(),
112                format: format.to_string(),
113                ..Default::default()
114            })
115        }
116
117        Ok(report_files)
118    }
119
120    fn compute_transformers(
121        &self,
122        metadata: &CoverageMetadata,
123    ) -> Result<Vec<Box<dyn Transformer>>> {
124        let mut transformers: Vec<Box<dyn Transformer>> = vec![];
125
126        transformers.push(Box::new(ComputeSummary::new()));
127
128        if let Some(prefix) = self.settings.strip_prefix.clone() {
129            transformers.push(Box::new(StripPrefix::new(prefix)));
130        } else {
131            transformers.push(Box::new(StripPrefix::default()));
132        }
133
134        transformers.push(Box::new(StripDotSlashPrefix));
135
136        if self.config.coverage.ignores.is_some() {
137            transformers.push(Box::new(IgnorePaths::new(
138                &self.config.coverage.ignores.as_ref().unwrap(),
139            )?));
140        }
141
142        if let Some(prefix) = self.settings.add_prefix.clone() {
143            transformers.push(Box::new(AddPrefix::new(&prefix)));
144        }
145
146        transformers.push(Box::new(AppendMetadata::new(metadata)));
147        Ok(transformers)
148    }
149}