xmlparser/
error.rs

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