Skip to main content

rapx/check/opt/
report.rs

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}