Skip to main content

ephemeris_report/
render.rs

1use crate::{Details, Outcome, Report, SkipReason};
2
3/// The verdict of a set of reports.
4///
5/// The verdict is [`Outcome::Fail`] when any report failed. Skipped checks do
6/// not fail it.
7#[must_use]
8pub fn verdict(reports: &[Report]) -> Outcome {
9    if reports.iter().any(|report| !report.passed()) {
10        Outcome::Fail
11    } else {
12        Outcome::Pass
13    }
14}
15
16/// A markdown list with one line per report, in input order.
17#[must_use]
18pub fn markdown(reports: &[Report]) -> String {
19    if reports.is_empty() {
20        return "No reports were found.\n".to_owned();
21    }
22    reports.iter().map(line).collect()
23}
24
25fn line(report: &Report) -> String {
26    let detail = detail(report);
27    let (check, outcome) = (&report.check, report.outcome);
28    if detail.is_empty() {
29        format!("- {check}: {outcome}\n")
30    } else {
31        format!("- {check}: {outcome} ({detail})\n")
32    }
33}
34
35fn detail(report: &Report) -> String {
36    let parts = match &report.details {
37        Some(Details::Tests { summary, items: _ }) => vec![
38            count(summary.passed, "passed", "passed"),
39            count(summary.failed, "failed", "failed"),
40            count(summary.skipped, "skipped", "skipped"),
41        ],
42        Some(Details::Diagnostics { summary, items: _ }) => vec![
43            count(summary.errors, "error", "errors"),
44            count(summary.warnings, "warning", "warnings"),
45        ],
46        Some(Details::Diff { items }) => {
47            vec![count(items.len() as u64, "file differs", "files differ")]
48        }
49        None => vec![plain(report)],
50    };
51    parts.into_iter().flatten().collect::<Vec<_>>().join(", ")
52}
53
54fn plain(report: &Report) -> Option<String> {
55    match (report.skipped, report.outcome, report.status) {
56        (Some(SkipReason::NotApplicable), _, _) => Some("not applicable".to_owned()),
57        (Some(SkipReason::NoOrigin), _, _) => Some("no origin".to_owned()),
58        (None, Outcome::Fail, Some(status)) => Some(format!("status {status}")),
59        (None, _, _) => None,
60    }
61}
62
63fn count(number: u64, one: &str, many: &str) -> Option<String> {
64    match number {
65        0 => None,
66        1 => Some(format!("1 {one}")),
67        _ => Some(format!("{number} {many}")),
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::{markdown, verdict};
74    use crate::{Outcome, Report, SkipReason};
75
76    fn report(json: &str) -> Report {
77        facet_json::from_str(json).expect("report literal")
78    }
79
80    #[test]
81    fn a_failure_decides_the_verdict_and_skips_do_not() {
82        let pass = Report::plain("a", Outcome::Pass, Some(0));
83        let skip = Report::skipped("b", SkipReason::NoOrigin);
84        let fail = Report::plain("c", Outcome::Fail, Some(1));
85        assert_eq!(verdict(&[]), Outcome::Pass);
86        assert_eq!(verdict(&[pass.clone(), skip.clone()]), Outcome::Pass);
87        assert_eq!(verdict(&[pass, skip, fail]), Outcome::Fail);
88    }
89
90    #[test]
91    fn markdown_has_one_line_per_report_in_order() {
92        let reports = [
93            report(
94                r#"{"check":"nextest","outcome":"pass","details":{"kind":"tests","summary":{"passed":305,"failed":0,"skipped":2},"items":[]}}"#,
95            ),
96            report(
97                r#"{"check":"clippy","outcome":"fail","details":{"kind":"diagnostics","summary":{"errors":3,"warnings":0},"items":[]}}"#,
98            ),
99            report(r#"{"check":"semver","outcome":"skip","skipped":"no-origin"}"#),
100            report(r#"{"check":"lint","outcome":"fail","status":42}"#),
101            report(r#"{"check":"docs","outcome":"pass","status":0}"#),
102        ];
103        assert_eq!(
104            markdown(&reports),
105            "- nextest: pass (305 passed, 2 skipped)\n- clippy: fail (3 errors)\n- semver: skip \
106             (no origin)\n- lint: fail (status 42)\n- docs: pass\n"
107        );
108    }
109
110    #[test]
111    fn markdown_says_when_there_are_no_reports() {
112        assert_eq!(markdown(&[]), "No reports were found.\n");
113    }
114}