1use annotate_snippets::{Level, Renderer, Snippet};
2
3use crate::utils::span::{relative_pos_range, span_to_filename, span_to_line_number, span_to_source_code};
4use rustc_span::Span;
5
6#[must_use]
7pub struct OptReport {
8 code_source: String,
9 filename: String,
10 line_start: usize,
11 rel_span: Span,
12 annotations: Vec<(Level, Span, String)>,
13 title: String,
14 title_level: Level,
15 footer: Option<String>,
16}
17
18impl OptReport {
19 pub fn new(base_span: Span, rel_span: Span) -> Self {
20 let code_source = span_to_source_code(base_span);
21 let filename = span_to_filename(base_span);
22 let line_start = span_to_line_number(base_span);
23 Self {
24 code_source,
25 filename,
26 line_start,
27 rel_span,
28 annotations: Vec::new(),
29 title: String::new(),
30 title_level: Level::Warning,
31 footer: None,
32 }
33 }
34
35 pub fn from_graph(graph: &crate::analysis::dataflow::Graph) -> Self {
36 Self::new(graph.span, graph.span)
37 }
38
39 pub fn file_name(mut self, span: Span) -> Self {
40 self.filename = span_to_filename(span);
41 self
42 }
43
44 pub fn title(mut self, title: impl Into<String>) -> Self {
45 self.title = title.into();
46 self
47 }
48
49 pub fn message_level(mut self, level: Level) -> Self {
50 self.title_level = level;
51 self
52 }
53
54 pub fn annotate(mut self, level: Level, span: Span, label: impl Into<String>) -> Self {
55 self.annotations.push((level, span, label.into()));
56 self
57 }
58
59 pub fn footer(mut self, footer: impl Into<String>) -> Self {
60 self.footer = Some(footer.into());
61 self
62 }
63
64 pub fn emit(&self) {
65 let mut snippet = Snippet::source(&self.code_source)
66 .line_start(self.line_start)
67 .origin(&self.filename)
68 .fold(true);
69 for (level, span, label) in &self.annotations {
70 snippet = snippet.annotation(level.span(relative_pos_range(self.rel_span, *span)).label(label));
71 }
72 let message = if let Some(ref footer) = self.footer {
73 self.title_level
74 .title(&self.title)
75 .snippet(snippet)
76 .footer(Level::Help.title(footer.as_str()))
77 } else {
78 self.title_level
79 .title(&self.title)
80 .snippet(snippet)
81 };
82 let renderer = Renderer::styled();
83 rap_warn!("{}", renderer.render(message));
84 }
85}