faster_pest/
error.rs

1const RED: &str = "\x1b[31;1m";
2const NORMAL: &str = "\x1b[0m";
3const BLUE: &str = "\x1b[34;1m";
4const BOLD: &str = "\x1b[1m";
5
6#[derive(Debug)]
7pub enum ErrorKind {
8    ExpectedValue(&'static str),
9    Expected(&'static str),
10    NegPredFailed(&'static str),
11    All(Vec<Error>)
12}
13
14impl std::fmt::Display for ErrorKind {
15    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
16        match self {
17            ErrorKind::ExpectedValue(expected) => write!(f, "Expected value: {expected}"),
18            ErrorKind::Expected(expected) => write!(f, "Expected: {expected}"),
19            ErrorKind::NegPredFailed(not_expected) => write!(f, "Negated predicate failed: {not_expected}"),
20            ErrorKind::All(errors) => write!(f, "All {} accepted patterns fail to match", errors.len()),
21        }
22    }
23}
24
25#[derive(Debug)]
26pub struct Error {
27    kind: ErrorKind,
28    remaining_bytes: usize,
29    trace: Vec<String>,
30    note: Option<String>,
31}
32
33impl Error {
34    pub fn new(kind: ErrorKind, input: &str, root: impl Into<String>) -> Error {
35        Error {
36            kind,
37            remaining_bytes: input.len(),
38            trace: vec![root.into()],
39            note: None,
40        }
41    }
42
43    pub fn with_trace(mut self, trace: impl Into<String>) -> Self {
44        self.trace.push(trace.into());
45        self
46    }
47
48    pub fn with_note(mut self, note: impl Into<String>) -> Self {
49        if self.note.is_none() {
50            self.note = Some(note.into());
51        }
52        self
53    }
54
55    pub fn print(&self, input: &str) {
56        if self.remaining_bytes > input.len() {
57            panic!("Error::print: remaining_bytes is greater than input length");
58        }
59        let position = input.len() - self.remaining_bytes;
60
61        let line_start = input[..position].rfind('\n').map(|i| i + 1).unwrap_or(0);
62        let line_end = input[position..].find('\n').map(|i| i + position).unwrap_or(input.len());
63        let line_number = input[..position].matches('\n').count() + 1;
64        let position_in_utf8_line = input[line_start..position].chars().count();
65
66        println!("{RED}error{NORMAL}: {}", self.kind);
67        println!("{BLUE}  -->{NORMAL} {}:{}:{}", self.trace[0], line_number, position - line_start + 1);
68        println!("{BLUE}   |{NORMAL}");
69        println!("{BLUE}{:<3}|{NORMAL} {}", line_number, &input[line_start..line_end]);
70        println!("{BLUE}   |{NORMAL} {}{RED}^{NORMAL}", " ".repeat(position_in_utf8_line));
71        if let Some(note) = &self.note {
72            println!("   {BLUE}= {NORMAL}{BOLD}note{NORMAL}: {note}");
73        }
74        println!("   {BLUE}= {NORMAL}{BOLD}trace{NORMAL}: {}", self.trace.join(", "));
75    }
76
77    pub fn into_pest<Rule: pest::RuleType>(self, input: &str) -> pest::error::Error<Rule> {
78        pest::error::Error::new_from_pos(
79            pest::error::ErrorVariant::CustomError {
80                message: format!("{}", self.kind),
81            },
82            pest::Position::new(input, input.len() - self.remaining_bytes).unwrap()
83        )
84    }
85}