Skip to main content

cratestack_parser/
diagnostics.rs

1use std::ops::Range;
2
3use ariadne::{Color, Label, Report, ReportKind, Source};
4use cratestack_core::SourceSpan;
5
6#[derive(Debug, Clone, thiserror::Error)]
7#[error("{message}")]
8pub struct SchemaError {
9    message: String,
10    span: Range<usize>,
11    line: usize,
12}
13
14impl SchemaError {
15    pub(crate) fn new(message: impl Into<String>, span: Range<usize>, line: usize) -> Self {
16        Self {
17            message: message.into(),
18            span,
19            line,
20        }
21    }
22
23    pub fn message(&self) -> &str {
24        &self.message
25    }
26
27    pub fn span(&self) -> Range<usize> {
28        self.span.clone()
29    }
30
31    pub fn line(&self) -> usize {
32        self.line
33    }
34
35    pub fn render(&self, path: &str, source: &str) -> String {
36        let mut output = Vec::new();
37        Report::build(ReportKind::Error, (path.to_owned(), self.span.clone()))
38            .with_message(&self.message)
39            .with_label(
40                Label::new((path.to_owned(), self.span.clone()))
41                    .with_message(&self.message)
42                    .with_color(Color::Red),
43            )
44            .finish()
45            .write((path.to_owned(), Source::from(source)), &mut output)
46            .expect("diagnostic rendering should succeed");
47
48        String::from_utf8(output).expect("ariadne should emit utf-8")
49    }
50}
51
52pub(crate) fn span_error(message: impl Into<String>, span: SourceSpan) -> SchemaError {
53    SchemaError::new(message, span.start..span.end, span.line)
54}