embedded_runner/
collect.rs1use std::path::PathBuf;
2
3use tokio::io::AsyncWriteExt;
4
5use crate::{cfg::CollectCmdConfig, coverage, RunnerError};
6
7pub async fn run(cfg: CollectCmdConfig) -> Result<(), RunnerError> {
8 let coverages_file = coverage::coverages_filepath();
9 let mut coverages = mantra_schema::coverage::CoverageSchema {
10 version: Some(mantra_schema::SCHEMA_VERSION.to_string()),
11 test_runs: Vec::new(),
12 };
13
14 if !coverages_file.exists() {
15 log::info!("No coverage to collect.");
16 return Ok(());
17 }
18
19 let files = tokio::fs::read_to_string(&coverages_file)
20 .await
21 .expect("Coverages file exists.");
22 for line in files.lines() {
23 let coverage_path = PathBuf::from(line);
24
25 if !coverage_path.exists() {
26 log::error!("Missing coverage file '{}'.", coverage_path.display());
27 } else {
28 let coverage_content = tokio::fs::read_to_string(coverage_path)
29 .await
30 .expect("Coverage file exists.");
31 let mut coverage: mantra_schema::coverage::CoverageSchema =
32 serde_json::from_str(&coverage_content)
33 .expect("Coverage was serialized with embedded-runner.");
34
35 coverages.test_runs.append(&mut coverage.test_runs);
36 }
37 }
38
39 if coverages.test_runs.is_empty() {
40 log::info!("No coverages found.");
41 return Ok(());
42 }
43
44 let output = match cfg.output {
45 Some(out) => {
46 if out
47 .extension()
48 .and_then(|s| s.to_str())
49 .map(|s| s.to_lowercase())
50 == Some("json".to_string())
51 {
52 out
53 } else {
54 return Err(RunnerError::Setup(
55 "Output path must point to a JSON file.".to_string(),
56 ));
57 }
58 }
59 None => PathBuf::from("coverage.json"),
60 };
61
62 let combined_coverage =
63 serde_json::to_string(&coverages).expect("Serializing coverage schema.");
64
65 if !output.exists() {
66 let mut file = tokio::fs::File::create(output).await.map_err(|_| {
67 RunnerError::Setup("Could not create combined coverage file.".to_string())
68 })?;
69 let _ = file.write(combined_coverage.as_bytes()).await;
70 } else {
71 let _ = tokio::fs::write(output, combined_coverage).await;
72 }
73
74 let _ = tokio::fs::remove_file(coverages_file).await;
76
77 Ok(())
78}