vimwiki_core/lang/parsers/
errors.rs

1use super::Span;
2use nom::error::{ContextError, ErrorKind, FromExternalError, ParseError};
3use std::{borrow::Cow, fmt};
4
5/// Represents an encapsulated error that is encountered
6#[derive(Clone, Debug, Default, Eq, PartialEq)]
7pub struct LangParserError<'a> {
8    ctx: Cow<'a, str>,
9    input: Span<'a>,
10    next: Option<Box<Self>>,
11}
12
13impl<'a> From<nom::Err<LangParserError<'a>>> for LangParserError<'a> {
14    fn from(nom_err: nom::Err<LangParserError<'a>>) -> Self {
15        match nom_err {
16            nom::Err::Error(x) | nom::Err::Failure(x) => x,
17            nom::Err::Incomplete(_) => {
18                Self::from_ctx(&Span::default(), "Incomplete")
19            }
20        }
21    }
22}
23
24impl<'a> fmt::Display for LangParserError<'a> {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        // Display our context along with the starting line/column
27        // NOTE: This is an expensive operation to calculate the line/column
28        writeln!(
29            f,
30            "{}: Line {}, Column {}",
31            self.ctx,
32            self.input.line(),
33            self.input.column()
34        )?;
35
36        // Produce the first line of our input, limiting to no more than
37        // 100 characters to prevent really long lines
38        writeln!(
39            f,
40            "{}",
41            &self
42                .input
43                .as_unsafe_remaining_str()
44                .lines()
45                .next()
46                .unwrap_or_default()
47                .chars()
48                .take(100)
49                .collect::<String>()
50        )?;
51
52        if let Some(next) = self.next.as_ref() {
53            next.fmt(f)?;
54        }
55
56        Ok(())
57    }
58}
59
60impl<'a> std::error::Error for LangParserError<'a> {}
61
62impl<'a> LangParserError<'a> {
63    pub fn unsupported() -> Self {
64        Self {
65            ctx: Cow::from("Unsupported"),
66            input: Span::from(""),
67            next: None,
68        }
69    }
70
71    pub fn from_ctx(input: &Span<'a>, ctx: &'static str) -> Self {
72        Self {
73            ctx: Cow::from(ctx),
74            input: *input,
75            next: None,
76        }
77    }
78}
79
80impl<'a, E> FromExternalError<Span<'a>, E> for LangParserError<'a> {
81    fn from_external_error(input: Span<'a>, kind: ErrorKind, _e: E) -> Self {
82        // TODO: Support unique external error rendering
83        Self::from_error_kind(input, kind)
84    }
85}
86
87impl<'a> ParseError<Span<'a>> for LangParserError<'a> {
88    fn from_error_kind(input: Span<'a>, kind: ErrorKind) -> Self {
89        Self {
90            ctx: Cow::from(kind.description().to_string()),
91            input,
92            next: None,
93        }
94    }
95
96    fn append(input: Span<'a>, kind: ErrorKind, other: Self) -> Self {
97        let mut e = Self::from_error_kind(input, kind);
98        e.next = Some(Box::new(other));
99        e
100    }
101
102    fn from_char(input: Span<'a>, c: char) -> Self {
103        Self {
104            ctx: Cow::from(format!("Char {}", c)),
105            input,
106            next: None,
107        }
108    }
109
110    fn or(self, other: Self) -> Self {
111        // Pick error that has progressed further
112        if self.input.start_offset() > other.input.start_offset() {
113            self
114        } else {
115            other
116        }
117    }
118}
119
120impl<'a> ContextError<Span<'a>> for LangParserError<'a> {
121    fn add_context(input: Span<'a>, ctx: &'static str, other: Self) -> Self {
122        Self {
123            ctx: Cow::from(ctx),
124            input,
125            next: Some(Box::new(other)),
126        }
127    }
128}