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