diskplan_schema/text/
error.rs

1use std::fmt::Display;
2
3/// A detailed error for an issue encountered during parsing
4#[derive(Debug, PartialEq)]
5pub struct ParseError<'a> {
6    error: String,
7    text: &'a str,
8    span: &'a str,
9    next: Option<Box<ParseError<'a>>>,
10}
11
12impl Display for ParseError<'_> {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        let lineno = self.line_number();
15        let line = self.text.lines().nth(lineno - 1).unwrap_or("<EOF>");
16        let column = self.span.as_ptr() as usize - line.as_ptr() as usize;
17        writeln!(f, "Error: {}", self.error)?;
18        writeln!(f, "     |")?;
19        writeln!(f, "{lineno:4} | {line}")?;
20        if column == 0 {
21            writeln!(f, "     |")?;
22        } else {
23            writeln!(f, "     | {0:1$}^", "", column)?;
24        }
25        if let Some(next) = &self.next {
26            write!(f, "{next}")?;
27        }
28        Ok(())
29    }
30}
31
32impl std::error::Error for ParseError<'_> {}
33
34impl<'a> ParseError<'a> {
35    /// Constructs a detailed error for an issue encountered during parsing
36    pub fn new(
37        error: String,
38        text: &'a str,
39        span: &'a str,
40        next: Option<Box<ParseError<'a>>>,
41    ) -> ParseError<'a> {
42        ParseError {
43            error,
44            text,
45            span,
46            next,
47        }
48    }
49
50    /// Returns the calculated line number of the span within the text
51    pub fn line_number(&self) -> usize {
52        let pos = self.span.as_ptr() as usize - self.text.as_ptr() as usize;
53        self.text[..pos].chars().filter(|&c| c == '\n').count() + 1
54    }
55}
56
57impl<'a, 'b> IntoIterator for &'b ParseError<'a> {
58    type IntoIter = ParseErrorIter<'a, 'b>;
59    type Item = &'b ParseError<'a>;
60
61    fn into_iter(self) -> Self::IntoIter {
62        ParseErrorIter { err: Some(self) }
63    }
64}
65
66pub struct ParseErrorIter<'a, 'b> {
67    err: Option<&'b ParseError<'a>>,
68}
69
70impl<'a, 'b> Iterator for ParseErrorIter<'a, 'b> {
71    type Item = &'b ParseError<'a>;
72
73    fn next(&mut self) -> Option<Self::Item> {
74        let cur = self.err;
75        if let Some(err) = cur {
76            self.err = err.next.as_deref();
77        }
78        cur
79    }
80}