Skip to main content

okane_core/report/
error.rs

1//! Defines error in report functions.
2
3use std::{fmt::Display, path::PathBuf};
4
5use annotate_snippets::{Annotation, Level, Snippet};
6use bumpalo::Bump;
7
8use crate::{
9    load,
10    parse::{self, ParsedSpan},
11};
12
13use super::{
14    book_keeping::{self, BookKeepError},
15    price_db,
16};
17
18/// Error arised in report APIs.
19#[derive(thiserror::Error, Debug)]
20pub enum ReportError {
21    Load(#[from] load::LoadError),
22    PriceDB(#[from] price_db::LoadError),
23    BookKeep(book_keeping::BookKeepError, Box<ErrorContext>),
24}
25
26impl Display for ReportError {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            ReportError::Load(_) => write!(f, "failed to load the given file"),
30            ReportError::PriceDB(_) => write!(f, "failed to load the Price DB"),
31            ReportError::BookKeep(err, ctx) => ctx.print(f, err),
32        }
33    }
34}
35
36/// Context of [ReportError], to carry the failure information.
37#[derive(Debug)]
38pub struct ErrorContext {
39    renderer: annotate_snippets::Renderer,
40    path: PathBuf,
41    line_start: usize,
42    text: String,
43    parsed_span: ParsedSpan,
44}
45
46impl ErrorContext {
47    fn print(&self, f: &mut std::fmt::Formatter<'_>, err: &BookKeepError) -> std::fmt::Result {
48        let message = err.to_string();
49        let path = self.path.to_string_lossy();
50        let bump = Bump::new();
51        let annotations: Vec<Annotation> = err.annotations(&bump, &self.parsed_span, &self.text);
52        let message = Level::ERROR.primary_title(&message).element(
53            Snippet::source(&self.text)
54                .path(&path)
55                .line_start(self.line_start)
56                .fold(false)
57                .annotations(annotations),
58        );
59        let rendered = self.renderer.render(&[message]);
60        rendered.fmt(f)
61    }
62
63    pub(super) fn new(
64        renderer: annotate_snippets::Renderer,
65        path: PathBuf,
66        pctx: &parse::ParsedContext,
67    ) -> Box<Self> {
68        Box::new(Self {
69            renderer,
70            path,
71            line_start: pctx.compute_line_start(),
72            text: pctx.as_str().to_owned(),
73            parsed_span: pctx.span(),
74        })
75    }
76}