cargo_tarpaulin/report/
html.rs1use crate::config::Config;
2use crate::errors::*;
3use crate::report::{get_previous_result, safe_json};
4use crate::traces::{Trace, TraceMap};
5use serde::Serialize;
6use std::fs::{read_to_string, File};
7use std::io::{self, Write};
8
9#[derive(Serialize)]
10struct SourceFile {
11 pub path: Vec<String>,
12 pub content: String,
13 pub traces: Vec<Trace>,
14 pub covered: usize,
15 pub coverable: usize,
16}
17
18#[derive(Serialize)]
19struct CoverageReport {
20 pub files: Vec<SourceFile>,
21}
22
23#[derive(PartialEq)]
24enum Context {
25 CurrentResults,
26 PreviousResults,
27}
28
29fn get_json(coverage_data: &TraceMap, context: Context) -> Result<String, RunError> {
30 let mut report = CoverageReport { files: Vec::new() };
31
32 for (path, traces) in coverage_data.iter() {
33 let content = match read_to_string(path) {
34 Ok(k) => k,
35 Err(e) => {
36 if context == Context::PreviousResults && e.kind() == io::ErrorKind::NotFound {
37 continue;
39 }
40
41 return Err(RunError::Html(format!(
42 "Unable to read source file to string: {e}"
43 )));
44 }
45 };
46
47 report.files.push(SourceFile {
48 path: path
49 .components()
50 .map(|c| c.as_os_str().to_string_lossy().to_string())
51 .collect(),
52 content,
53 traces: traces.clone(),
54 covered: coverage_data.covered_in_path(path),
55 coverable: coverage_data.coverable_in_path(path),
56 });
57 }
58
59 safe_json::to_string_safe(&report)
60 .map_err(|e| RunError::Html(format!("Report isn't serializable: {e}")))
61}
62
63pub fn export(coverage_data: &TraceMap, config: &Config) -> Result<(), RunError> {
64 let file_path = config.output_dir().join("tarpaulin-report.html");
65 let mut file = match File::create(file_path) {
66 Ok(k) => k,
67 Err(e) => return Err(RunError::Html(format!("File is not writeable: {e}"))),
68 };
69
70 let report_json = get_json(coverage_data, Context::CurrentResults)?;
71 let previous_report_json = match get_previous_result(config) {
72 Some(result) => get_json(&result, Context::PreviousResults)?,
73 None => String::from("null"),
74 };
75
76 match write!(
77 file,
78 r##"<!doctype html>
79<html>
80<head>
81 <meta charset="utf-8">
82 <style>{}</style>
83</head>
84<body>
85 <div id="root"></div>
86 <script>
87 var data = {};
88 var previousData = {};
89 </script>
90 <script crossorigin>{}</script>
91 <script crossorigin>{}</script>
92 <script>{}</script>
93</body>
94</html>"##,
95 include_str!("report_viewer.css"),
96 report_json,
97 previous_report_json,
98 include_str!("react.production.min.js"),
99 include_str!("react-dom.production.min.js"),
100 include_str!("report_viewer.js"),
101 ) {
102 Ok(_) => (),
103 Err(e) => return Err(RunError::Html(e.to_string())),
104 };
105
106 Ok(())
107}