kind_report/report/mode/
compact.rs

1use std::{fmt::Write, path::PathBuf};
2
3use pathdiff::diff_paths;
4
5use crate::data::*;
6use crate::{report::*, RenderConfig};
7
8use super::{CodeBlock, Compact, Renderable, Res};
9
10fn mark_code(
11    markers: &mut [&(Point, Point, &Marker)],
12    code_line: &str,
13    fmt: &mut dyn Write,
14) -> std::fmt::Result {
15    markers.sort_by(|x, y| x.0.column.cmp(&y.0.column));
16    let mut start = 0;
17
18    for marker in markers {
19        if start < marker.0.column {
20            write!(fmt, "{}", &code_line[start..marker.0.column])?;
21            start = marker.0.column;
22        }
23
24        let end = if marker.0.line == marker.1.line {
25            marker.1.column
26        } else {
27            code_line.len()
28        };
29
30        if start < end {
31            write!(fmt, "{{{{{}}}}}", &code_line[start..end])?;
32            start = end;
33        }
34    }
35
36    if start < code_line.len() {
37        write!(fmt, "{}", &code_line[start..code_line.len()])?;
38    }
39
40    writeln!(fmt)?;
41    Ok(())
42}
43
44impl Renderable<Compact> for Word {
45    fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res {
46        match self {
47            Word::Normal(str) => write!(fmt, "{} ", str),
48            Word::Dimmed(str) => write!(fmt, "{} ", str),
49            Word::White(str) => write!(fmt, "{} ", str),
50            Word::Painted(_, str) => write!(fmt, "{} ", str),
51        }
52    }
53}
54
55impl Renderable<Compact> for Subtitle {
56    fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res {
57        match self {
58            Subtitle::Field(_, phr) => writeln!(fmt, "{}", phr.to_lowercase()),
59            Subtitle::Normal(_, phr) => writeln!(fmt, "- {}", phr),
60            Subtitle::Bold(_, phr) => writeln!(fmt, "- {}", phr),
61            Subtitle::Phrase(_, words) => {
62                write!(fmt, "- ")?;
63                Renderable::<Compact>::render(words, fmt, cache, config)?;
64                writeln!(fmt)
65            }
66            Subtitle::LineBreak => Ok(()),
67        }
68    }
69}
70
71impl<'a> Renderable<Compact> for Subtitles<'a> {
72    fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res {
73        if !self.0.is_empty() {
74            writeln!(fmt)?;
75        }
76
77        Renderable::<Compact>::render(self.0, fmt, cache, config)
78    }
79}
80
81impl Renderable<Compact> for Severity {
82    fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res {
83        use Severity::*;
84
85        let painted = match self {
86            Error => "error:",
87            Warning => "warn:",
88            Info => "info:",
89        };
90
91        write!(fmt, " {} ", painted)
92    }
93}
94
95impl<'a> Renderable<Compact> for Header<'a> {
96    fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res {
97        writeln!(fmt, "{}", &self.title.to_lowercase())
98    }
99}
100
101impl<'a> Renderable<Compact> for CodeBlock<'a> {
102    fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res {
103        writeln!(fmt, "location")?;
104        let guide = LineGuide::get(self.code);
105
106        let (lines_set, mut by_line, _) = group_marker_lines(&guide, self.markers);
107
108        let code_lines: Vec<&'a str> = self.code.lines().collect();
109
110        let mut lines: Vec<usize> = lines_set
111            .into_iter()
112            .filter(|x| *x < code_lines.len())
113            .collect();
114
115        lines.sort();
116
117        for line in lines {
118            let mut empty_vec = Vec::new();
119            let row = by_line.get_mut(&line).unwrap_or(&mut empty_vec);
120
121            let mut inline_markers: Vec<&(Point, Point, &Marker)> =
122                row.iter().filter(|x| x.0.line == x.1.line).collect();
123
124            if !inline_markers.is_empty() {
125                mark_code(&mut inline_markers, code_lines[line], fmt)?;
126            } else {
127                writeln!(fmt, "{}", code_lines[line])?;
128            }
129        }
130
131        Ok(())
132    }
133}
134
135impl<'a> Renderable<Compact> for Markers<'a> {
136    fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res {
137        let groups = group_markers(self.0);
138        let current = PathBuf::from(".").canonicalize().unwrap();
139
140        for (ctx, markers) in groups.iter() {
141            let (file, code) = cache.fetch(*ctx).unwrap();
142            let path = diff_paths(&file.clone(), current.clone()).unwrap_or(file);
143
144            let block = CodeBlock {
145                code,
146                path: &path,
147                markers,
148            };
149
150            Renderable::<Compact>::render(&block, fmt, cache, config)?;
151        }
152
153        Ok(())
154    }
155}
156
157impl Renderable<Compact> for DiagnosticFrame {
158    fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res {
159        Renderable::<Compact>::render(&self.header(), fmt, cache, config)?;
160        Renderable::<Compact>::render(&self.subtitles(), fmt, cache, config)?;
161        Renderable::<Compact>::render(&self.markers(), fmt, cache, config)?;
162
163        Ok(())
164    }
165}
166
167impl Renderable<Compact> for Log {
168    fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res {
169        match self {
170            Log::Compiled(_) => writeln!(fmt, "compiled"),
171            Log::Checked(_) => writeln!(fmt, "checked"),
172            _ => Ok(()),
173        }
174    }
175}