Skip to main content

rapx/check/opt/
report.rs

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}