Skip to main content

devscout_rs/render/
coverage.rs

1use crate::query;
2
3use super::markers::{compact_marker, heuristic_suffix, test_via_suffix};
4
5/// Default `devscout tests` output. The zero case is a first-class answer, not
6/// an empty table: "nothing tests this" is exactly what the caller asked, and it
7/// is one line rather than a header over a void.
8///
9/// Heuristic file lines carry the SAME word-suffix the refs/impact renderers
10/// use, and the summary counts stay precise-only, so a file listed under a
11/// covered count is a file a test really references.
12pub fn render_tests_text(model: &query::TestsModel) -> String {
13    let mut out: Vec<String> = vec![format!("tests for {}", model.symbol)];
14    if model.rows.is_empty() {
15        out.push("no test references found".to_string());
16        return out.join("\n");
17    }
18    out.push(format!(
19        "covered by {} test file(s), {} reference(s)",
20        model.test_file_count, model.ref_count
21    ));
22    out.push(String::new());
23    for r in &model.rows {
24        out.push(format!(
25            "{}{}{}",
26            r.file,
27            heuristic_suffix(r.heuristic, r.tier),
28            test_via_suffix(r.via)
29        ));
30        let lines = r
31            .lines
32            .iter()
33            .map(|l| l.to_string())
34            .collect::<Vec<_>>()
35            .join(", ");
36        if r.test_defs.is_empty() {
37            // A `Project`-vouched row can carry no attributed def at all --
38            // still one line, so the file's referencing lines are never
39            // silently dropped.
40            out.push(format!("  lines: {lines}"));
41        } else {
42            for def_id in &r.test_defs {
43                out.push(format!("  {def_id}  lines: {lines}"));
44            }
45        }
46    }
47    out.join("\n")
48}
49
50/// `--compact` `devscout tests` output. The same model with the def ids and the
51/// per-file indentation dropped: one header line carrying every count, then one
52/// line per file. The `x`/`h` line marker is the compact renderer's existing
53/// convention for a guess.
54pub fn render_tests_compact(model: &query::TestsModel) -> String {
55    let heur = if model.heuristic_file_count != 0 {
56        format!(" heuristic={}", model.heuristic_file_count)
57    } else {
58        String::new()
59    };
60    let mut out: Vec<String> = vec![format!(
61        "tests {} files={} refs={}{heur}",
62        model.symbol, model.test_file_count, model.ref_count
63    )];
64    for r in &model.rows {
65        let lines = r
66            .lines
67            .iter()
68            .map(|l| format!("{l}{}", compact_marker(r.heuristic, r.tier)))
69            .collect::<Vec<_>>()
70            .join(",");
71        out.push(format!("{} {lines}", r.file));
72    }
73    out.join("\n")
74}