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}