diff-coverage 0.6.1

Diff-coverage, supercharged in Rust. Fast, memory-efficient coverage on changed lines for CI.
use std::io::Write;

use serde::Serialize;

use super::{CoverageReport, ReportGenerator};

#[derive(Serialize)]
struct JsonReport {
    total_changed: usize,
    total_covered: usize,
    coverage_percent: f64,
    uncovered_files: Vec<JsonFile>,
}

#[derive(Serialize)]
struct JsonFile {
    path: String,
    uncovered_lines: Vec<u32>,
}

pub struct JsonReportGenerator;

impl ReportGenerator for JsonReportGenerator {
    fn write_report(&self, report: &CoverageReport, out: &mut dyn Write) -> Result<(), String> {
        let payload = render_report(report)?;
        out.write_all(payload.as_bytes())
            .map_err(|err| err.to_string())
    }
}

fn render_report(report: &CoverageReport) -> Result<String, String> {
    let uncovered_files = report
        .uncovered_files
        .iter()
        .map(|file| JsonFile {
            path: file.path.clone(),
            uncovered_lines: file.uncovered_lines.clone(),
        })
        .collect();
    let payload = JsonReport {
        total_changed: report.total_changed,
        total_covered: report.total_covered,
        coverage_percent: report.coverage_percent(),
        uncovered_files,
    };
    let mut text = serde_json::to_string_pretty(&payload).map_err(|err| err.to_string())?;
    text.push('\n');
    Ok(text)
}

#[cfg(test)]
mod tests {
    use super::JsonReportGenerator;
    use crate::report::ReportGenerator;
    use crate::report::{CoverageReport, UncoveredFile};
    use serde_json::Value;

    #[test]
    fn writes_simple_json_report() {
        let report = CoverageReport {
            total_changed: 4,
            total_covered: 2,
            uncovered_files: vec![UncoveredFile {
                path: "src/foo.rs".to_string(),
                uncovered_lines: vec![2, 3],
                covered_lines: 2,
                changed_lines: 4,
            }],
            skipped_files: Vec::new(),
        };

        let mut out = Vec::new();
        JsonReportGenerator
            .write_report(&report, &mut out)
            .expect("write report");

        let content = String::from_utf8(out).expect("utf8");
        assert!(content.ends_with('\n'));
        let payload: Value = serde_json::from_str(&content).expect("parse json");
        assert_eq!(payload["total_changed"], 4);
        assert_eq!(payload["total_covered"], 2);
        assert!(payload["coverage_percent"].as_f64().is_some());
        assert_eq!(payload["uncovered_files"][0]["path"], "src/foo.rs");
        assert_eq!(payload["uncovered_files"][0]["uncovered_lines"][0], 2);
    }
}