wikitext_parser/
error.rs

1use std::error::Error;
2use std::fmt::Display;
3
4use crate::tokenizer::TextPosition;
5use crate::wikitext::TextFormatting;
6
7pub type Result<T> = std::result::Result<T, ParserError>;
8
9/// Error type of this crate.
10#[derive(Debug, Eq, PartialEq)]
11pub struct ParserError {
12    /// The kind of error.
13    pub kind: ParserErrorKind,
14    /// The position of the error in text.
15    pub position: TextPosition,
16    /// Further information about the error.
17    pub annotations: Vec<String>,
18}
19
20/// The kind of parser error.
21#[derive(Debug, Eq, PartialEq)]
22pub enum ParserErrorKind {
23    /// Found a second root section, but only one is allowed.
24    SecondRootSection {
25        /// The label of the second root section.
26        label: String,
27    },
28
29    /// Found a section at a level that is deeper than supported.
30    SectionLevelTooDeep {
31        /// The too deep level.
32        level: usize,
33    },
34
35    /// Found a double close brace that does not match any opened one.
36    UnmatchedDoubleCloseBrace,
37
38    /// Found a double open brace that does not match any closed one.
39    UnmatchedDoubleOpenBrace,
40
41    /// Found a double close bracket that does not match any opened one.
42    UnmatchedDoubleCloseBracket,
43
44    /// Found a double open bracket that does not match any closed one.
45    UnmatchedDoubleOpenBracket,
46
47    /// Found a `</nowiki>` that does not match any `<nowiki>`.
48    UnmatchedNoWikiClose,
49
50    /// Found a `<nowiki>` that does not match any `</nowiki>`.
51    UnmatchedNoWikiOpen,
52
53    /// A tag contains a token that does not belong there.
54    UnexpectedTokenInTag {
55        /// The unexpected token.
56        token: String,
57    },
58
59    /// A parameter contains a token that does not belong there.
60    UnexpectedTokenInParameter {
61        /// The unexpected token.
62        token: String,
63    },
64
65    /// A link contains a token that does not belong there.
66    UnexpectedTokenInLink {
67        /// The unexpected token.
68        token: String,
69    },
70
71    /// A link label contains a token that does not belong there.
72    UnexpectedTokenInLinkLabel {
73        /// The unexpected token.
74        token: String,
75    },
76
77    /// A formatted piece of text contains a token that does not belong there.
78    UnexpectedTokenInFormattedText {
79        /// The unexpected token.
80        token: String,
81    },
82
83    /// A link label contains a token that does not belong there.
84    UnexpectedTokenInListItem {
85        /// The unexpected token.
86        token: String,
87    },
88
89    /// A token was found at a place where it does not belong.
90    UnexpectedToken {
91        /// The expected token, or a list of possible expected tokens.
92        expected: String,
93        /// The token that was found.
94        actual: String,
95    },
96
97    /// A text formatting expression was not closed.
98    UnclosedTextFormatting {
99        /// The unclosed formatting expression.
100        formatting: TextFormatting,
101    },
102
103    /// The end of file was found, but further tokens were expected.
104    UnexpectedEof,
105}
106
107impl Display for ParserErrorKind {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        match self {
110            ParserErrorKind::SecondRootSection { label } => {
111                write!(f, "found second root section {label:?}")
112            }
113            ParserErrorKind::SectionLevelTooDeep { level } => {
114                write!(f, "found a section of a too deep level {level}")
115            }
116            ParserErrorKind::UnmatchedDoubleCloseBrace => {
117                write!(f, "found an unmatched double closing brace }}}}")
118            }
119            ParserErrorKind::UnmatchedDoubleOpenBrace => {
120                write!(f, "found an unmatched double open brace {{{{")
121            }
122            ParserErrorKind::UnmatchedDoubleCloseBracket => {
123                write!(f, "found an unmatched double close bracket ]]")
124            }
125            ParserErrorKind::UnmatchedDoubleOpenBracket => {
126                write!(f, "found an unmatched double open bracket [[")
127            }
128            ParserErrorKind::UnmatchedNoWikiClose => {
129                write!(f, "found an unmatched nowiki close tag </nowiki>")
130            }
131            ParserErrorKind::UnmatchedNoWikiOpen => {
132                write!(f, "found an unmatched nowiki open tag <nowiki>")
133            }
134            ParserErrorKind::UnexpectedTokenInTag { token } => {
135                write!(f, "found an unexpected token {token:?} in a tag")
136            }
137            ParserErrorKind::UnexpectedTokenInParameter { token } => {
138                write!(f, "found an unexpected token {token:?} in a parameter")
139            }
140            ParserErrorKind::UnexpectedTokenInLink { token } => {
141                write!(f, "found an unexpected token {token:?} in a link")
142            }
143            ParserErrorKind::UnexpectedTokenInLinkLabel { token } => {
144                write!(f, "found an unexpected token {token:?} in a link label")
145            }
146            ParserErrorKind::UnexpectedTokenInFormattedText { token } => {
147                write!(f, "found an unexpected token {token:?} in formatted text")
148            }
149            ParserErrorKind::UnexpectedTokenInListItem { token } => {
150                write!(f, "found an unexpected token {token:?} in a list item")
151            }
152            ParserErrorKind::UnexpectedToken { expected, actual } => write!(
153                f,
154                "found an unexpected token {actual:?} where {expected:?} was expected"
155            ),
156            ParserErrorKind::UnclosedTextFormatting { formatting } => write!(
157                f,
158                "found an unclosed text formatting expression {formatting}:?"
159            ),
160            ParserErrorKind::UnexpectedEof => {
161                write!(f, "the file ended, but we expected more content")
162            }
163        }
164    }
165}
166
167impl Display for ParserError {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        write!(
170            f,
171            "{} at line {}, column {}",
172            self.kind, self.position.line, self.position.column
173        )?;
174
175        if !self.annotations.is_empty() {
176            write!(f, "; additional information: [")?;
177            let mut once = true;
178            for annotation in &self.annotations {
179                if once {
180                    once = false;
181                } else {
182                    write!(f, ", ")?;
183                }
184                write!(f, "{annotation}")?;
185            }
186            write!(f, "]")?;
187        }
188
189        Ok(())
190    }
191}
192
193impl Error for ParserError {}
194
195impl ParserError {
196    /// Add the given annotation to the error.
197    pub fn annotate(&mut self, annotation: String) {
198        self.annotations.push(annotation);
199    }
200
201    /// Add the given annotation to the error.
202    pub fn annotate_self(mut self, annotation: String) -> Self {
203        self.annotations.push(annotation);
204        self
205    }
206}
207
208impl ParserErrorKind {
209    pub(crate) fn into_parser_error(self, position: TextPosition) -> ParserError {
210        ParserError {
211            kind: self,
212            position,
213            annotations: Default::default(),
214        }
215    }
216}