rapx 0.7.31

A static analysis platform for Rust program analysis and verification
use annotate_snippets::{Level, Renderer, Snippet};

use crate::utils::span::{relative_pos_range, span_to_filename, span_to_line_number, span_to_source_code};
use rustc_span::Span;

#[must_use]
pub struct OptReport {
    code_source: String,
    filename: String,
    line_start: usize,
    rel_span: Span,
    annotations: Vec<(Level, Span, String)>,
    title: String,
    title_level: Level,
    footer: Option<String>,
}

impl OptReport {
    pub fn new(base_span: Span, rel_span: Span) -> Self {
        let code_source = span_to_source_code(base_span);
        let filename = span_to_filename(base_span);
        let line_start = span_to_line_number(base_span);
        Self {
            code_source,
            filename,
            line_start,
            rel_span,
            annotations: Vec::new(),
            title: String::new(),
            title_level: Level::Warning,
            footer: None,
        }
    }

    pub fn from_graph(graph: &crate::analysis::dataflow::Graph) -> Self {
        Self::new(graph.span, graph.span)
    }

    pub fn file_name(mut self, span: Span) -> Self {
        self.filename = span_to_filename(span);
        self
    }

    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    pub fn message_level(mut self, level: Level) -> Self {
        self.title_level = level;
        self
    }

    pub fn annotate(mut self, level: Level, span: Span, label: impl Into<String>) -> Self {
        self.annotations.push((level, span, label.into()));
        self
    }

    pub fn footer(mut self, footer: impl Into<String>) -> Self {
        self.footer = Some(footer.into());
        self
    }

    pub fn emit(&self) {
        let mut snippet = Snippet::source(&self.code_source)
            .line_start(self.line_start)
            .origin(&self.filename)
            .fold(true);
        for (level, span, label) in &self.annotations {
            snippet = snippet.annotation(level.span(relative_pos_range(self.rel_span, *span)).label(label));
        }
        let message = if let Some(ref footer) = self.footer {
            self.title_level
                .title(&self.title)
                .snippet(snippet)
                .footer(Level::Help.title(footer.as_str()))
        } else {
            self.title_level
                .title(&self.title)
                .snippet(snippet)
        };
        let renderer = Renderer::styled();
        rap_warn!("{}", renderer.render(message));
    }
}