htmlparser/
error.rs

1use core::{fmt, str};
2#[cfg(feature = "std")]
3use std::error;
4
5/// An XML parser errors.
6#[allow(missing_docs)]
7#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
8pub enum Error {
9    InvalidDeclaration(StreamError, TextPos),
10    InvalidConditionalComment(StreamError, TextPos),
11    InvalidComment(StreamError, TextPos),
12    InvalidPI(StreamError, TextPos),
13    InvalidDoctype(StreamError, TextPos),
14    InvalidEntity(StreamError, TextPos),
15    InvalidElement(StreamError, TextPos),
16    InvalidAttribute(StreamError, TextPos),
17    InvalidCdata(StreamError, TextPos),
18    InvalidCharData(StreamError, TextPos),
19    UnknownToken(TextPos),
20}
21
22impl Error {
23    /// Returns the error position.
24    pub fn pos(&self) -> TextPos {
25        match *self {
26            Error::InvalidDeclaration(_, pos) => pos,
27            Error::InvalidConditionalComment(_, pos) => pos,
28            Error::InvalidComment(_, pos) => pos,
29            Error::InvalidPI(_, pos) => pos,
30            Error::InvalidDoctype(_, pos) => pos,
31            Error::InvalidEntity(_, pos) => pos,
32            Error::InvalidElement(_, pos) => pos,
33            Error::InvalidAttribute(_, pos) => pos,
34            Error::InvalidCdata(_, pos) => pos,
35            Error::InvalidCharData(_, pos) => pos,
36            Error::UnknownToken(pos) => pos,
37        }
38    }
39}
40
41impl fmt::Display for Error {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        match *self {
44            Error::InvalidDeclaration(ref cause, pos) => {
45                write!(f, "invalid XML declaration at {} cause {}", pos, cause)
46            }
47            Error::InvalidConditionalComment(ref cause, pos) => {
48                write!(f, "invalid conditional comment at {} cause {}", pos, cause)
49            }
50            Error::InvalidComment(ref cause, pos) => {
51                write!(f, "invalid comment at {} cause {}", pos, cause)
52            }
53            Error::InvalidPI(ref cause, pos) => {
54                write!(
55                    f,
56                    "invalid processing instruction at {} cause {}",
57                    pos, cause
58                )
59            }
60            Error::InvalidDoctype(ref cause, pos) => {
61                write!(f, "invalid DTD at {} cause {}", pos, cause)
62            }
63            Error::InvalidEntity(ref cause, pos) => {
64                write!(f, "invalid DTD entity at {} cause {}", pos, cause)
65            }
66            Error::InvalidElement(ref cause, pos) => {
67                write!(f, "invalid element at {} cause {}", pos, cause)
68            }
69            Error::InvalidAttribute(ref cause, pos) => {
70                write!(f, "invalid attribute at {} cause {}", pos, cause)
71            }
72            Error::InvalidCdata(ref cause, pos) => {
73                write!(f, "invalid CDATA at {} cause {}", pos, cause)
74            }
75            Error::InvalidCharData(ref cause, pos) => {
76                write!(f, "invalid character data at {} cause {}", pos, cause)
77            }
78            Error::UnknownToken(pos) => {
79                write!(f, "unknown token at {}", pos)
80            }
81        }
82    }
83}
84
85#[cfg(feature = "std")]
86impl error::Error for Error {
87    fn description(&self) -> &str {
88        "an XML parsing error"
89    }
90}
91
92/// A stream parser errors.
93#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
94pub enum StreamError {
95    /// The steam ended earlier than we expected.
96    ///
97    /// Should only appear on invalid input data.
98    /// Errors in a valid XML should be handled by errors below.
99    UnexpectedEndOfStream,
100
101    /// An invalid name.
102    InvalidName,
103
104    /// A non-XML character has occurred.
105    ///
106    /// Valid characters are: <https://www.w3.org/TR/xml/#char32>
107    NonXmlChar(char, TextPos),
108
109    /// An invalid/unexpected character.
110    ///
111    /// The first byte is an actual one, the second one is expected.
112    ///
113    /// We are using a single value to reduce the struct size.
114    InvalidChar(u8, u8, TextPos),
115
116    /// An invalid/unexpected character.
117    ///
118    /// Just like `InvalidChar`, but specifies multiple expected characters.
119    InvalidCharMultiple(u8, &'static [u8], TextPos),
120
121    /// An unexpected character instead of `"` or `'`.
122    InvalidQuote(u8, TextPos),
123
124    /// An unexpected character instead of an XML space.
125    ///
126    /// Includes: `' ' \n \r \t &#x20; &#x9; &#xD; &#xA;`.
127    InvalidSpace(u8, TextPos),
128
129    /// An unexpected string.
130    ///
131    /// Contains what string was expected.
132    InvalidString(&'static str, TextPos),
133
134    /// An invalid reference.
135    InvalidReference,
136
137    /// An invalid ExternalID in the DTD.
138    InvalidExternalID,
139
140    /// Comment cannot contain `--`.
141    InvalidCommentData,
142
143    /// Comment cannot end with `-`.
144    InvalidCommentEnd,
145
146    /// A Character Data node contains an invalid data.
147    ///
148    /// Currently, only `]]>` is not allowed.
149    InvalidCharacterData,
150}
151
152impl fmt::Display for StreamError {
153    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        match *self {
155            StreamError::UnexpectedEndOfStream => {
156                write!(f, "unexpected end of stream")
157            }
158            StreamError::InvalidName => {
159                write!(f, "invalid name token")
160            }
161            StreamError::NonXmlChar(c, pos) => {
162                write!(f, "a non-XML character {:?} found at {}", c, pos)
163            }
164            StreamError::InvalidChar(actual, expected, pos) => {
165                write!(
166                    f,
167                    "expected '{}' not '{}' at {}",
168                    expected as char, actual as char, pos
169                )
170            }
171            StreamError::InvalidCharMultiple(actual, expected, pos) => {
172                let mut expected_iter = expected.iter().peekable();
173
174                write!(f, "expected ")?;
175                while let Some(&c) = expected_iter.next() {
176                    write!(f, "'{}'", c as char)?;
177                    if expected_iter.peek().is_some() {
178                        write!(f, ", ")?;
179                    }
180                }
181                write!(f, " not '{}' at {}", actual as char, pos)
182            }
183            StreamError::InvalidQuote(c, pos) => {
184                write!(f, "expected quote mark not '{}' at {}", c as char, pos)
185            }
186            StreamError::InvalidSpace(c, pos) => {
187                write!(f, "expected space not '{}' at {}", c as char, pos)
188            }
189            StreamError::InvalidString(expected, pos) => {
190                write!(f, "expected '{}' at {}", expected, pos)
191            }
192            StreamError::InvalidReference => {
193                write!(f, "invalid reference")
194            }
195            StreamError::InvalidExternalID => {
196                write!(f, "invalid ExternalID")
197            }
198            StreamError::InvalidCommentData => {
199                write!(f, "'--' is not allowed in comments")
200            }
201            StreamError::InvalidCommentEnd => {
202                write!(f, "comment cannot end with '-'")
203            }
204            StreamError::InvalidCharacterData => {
205                write!(f, "']]>' is not allowed inside a character data")
206            }
207        }
208    }
209}
210
211#[cfg(feature = "std")]
212impl error::Error for StreamError {
213    fn description(&self) -> &str {
214        "an XML stream parsing error"
215    }
216}
217
218/// Position in text.
219///
220/// Position indicates a row/line and a column in the original text. Starting from 1:1.
221#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
222#[allow(missing_docs)]
223pub struct TextPos {
224    pub row: u32,
225    pub col: u32,
226}
227
228impl TextPos {
229    /// Constructs a new `TextPos`.
230    ///
231    /// Should not be invoked manually, but rather via `Stream::gen_text_pos`.
232    pub fn new(row: u32, col: u32) -> TextPos {
233        TextPos { row, col }
234    }
235}
236
237impl fmt::Display for TextPos {
238    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239        write!(f, "{}:{}", self.row, self.col)
240    }
241}