Skip to main content

xml/reader/
error.rs

1use crate::reader::lexer::Token;
2use crate::writer::Error as EmitterError;
3use crate::Encoding;
4
5use std::borrow::Cow;
6use std::error::Error as _;
7use std::{error, fmt, io, str};
8
9use crate::common::{Position, TextPosition};
10use crate::util;
11
12/// Failure reason
13#[derive(Debug)]
14#[non_exhaustive]
15pub enum ErrorKind {
16    /// This is an ill-formed XML document
17    Syntax(Cow<'static, str>),
18    /// Reader/writer reported an error
19    Io(io::Error),
20    /// The document contains bytes that are not allowed in UTF-8 strings
21    Utf8(str::Utf8Error),
22    /// The document ended while they were elements/comments/etc. still open
23    UnexpectedEof,
24    /// [Writer error](crate::writer::Error) for convenience of using a single [`Error`] type
25    EmitterError(Box<EmitterError>),
26}
27
28/// Returned by `add_entities()`
29#[derive(Clone, PartialEq)]
30#[non_exhaustive]
31pub enum ImmutableEntitiesError {
32    /// Too late to modify
33    ElementEncountered,
34    /// `<?xml standalone="yes" ?>` can't have entities
35    StandaloneDocument,
36}
37
38#[derive(Debug, Clone, PartialEq)]
39#[non_exhaustive]
40pub(crate) enum SyntaxError {
41    CannotRedefineXmlnsPrefix,
42    CannotRedefineXmlPrefix,
43    /// Recursive custom entity expanded to too many chars, it could be DoS
44    EntityTooBig,
45    EmptyEntity,
46    NoRootElement,
47    ProcessingInstructionWithoutName,
48    UnbalancedRootElement,
49    UnexpectedEof,
50    UnexpectedOpeningTag,
51    /// Missing `]]>`
52    UnclosedCdata,
53    UnexpectedQualifiedName(Token),
54    UnexpectedTokenOutsideRoot(Token),
55    UnexpectedToken(Token),
56    UnexpectedTokenInEntity(Token),
57    UnexpectedTokenInClosingTag(Token),
58    UnexpectedTokenInOpeningTag(Token),
59    InvalidQualifiedName(Box<str>),
60    UnboundAttribute(Box<str>),
61    UnboundElementPrefix(Box<str>),
62    UnexpectedClosingTag(Box<str>),
63    UnexpectedName(Box<str>),
64    /// Found <?xml-like PI not at the beginning of a document,
65    /// which is an error, see section 2.6 of XML 1.1 spec
66    UnexpectedProcessingInstruction(Box<str>, Token),
67    CannotUndefinePrefix(Box<str>),
68    InvalidCharacterEntity(u32),
69    InvalidDefaultNamespace(Box<str>),
70    InvalidNamePrefix(Box<str>),
71    InvalidNumericEntity(Box<str>),
72    InvalidStandaloneDeclaration(Box<str>),
73    InvalidXmlProcessingInstruction(Box<str>),
74    RedefinedAttribute(Box<str>),
75    UndefinedEntity(Box<str>),
76    UnexpectedEntity(Box<str>),
77    UnexpectedNameInsideXml(Box<str>),
78    UnsupportedEncoding(Box<str>),
79    /// In DTD
80    UnknownMarkupDeclaration(Box<str>),
81    UnexpectedXmlVersion(Box<str>),
82    ConflictingEncoding(Encoding, Encoding),
83    UnexpectedTokenBefore(&'static str, char),
84    /// Document has more stuff than `ParserConfig` allows
85    ExceededConfiguredLimit,
86}
87
88impl fmt::Display for SyntaxError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        self.to_cow().fmt(f)
91    }
92}
93
94impl SyntaxError {
95    #[inline(never)]
96    #[cold]
97    pub(crate) fn to_cow(&self) -> Cow<'static, str> {
98        match *self {
99            Self::CannotRedefineXmlnsPrefix => "Cannot redefine XMLNS prefix".into(),
100            Self::CannotRedefineXmlPrefix => "Default XMLNS prefix cannot be rebound to another value".into(),
101            Self::EmptyEntity => "Encountered empty entity".into(),
102            Self::EntityTooBig => "Entity too big".into(),
103            Self::NoRootElement => "Unexpected end of stream: no root element found".into(),
104            Self::ProcessingInstructionWithoutName => "Encountered processing instruction without a name".into(),
105            Self::UnbalancedRootElement => "Unexpected end of stream: still inside the root element".into(),
106            Self::UnclosedCdata => "Unclosed <![CDATA[".into(),
107            Self::UnexpectedEof => "Unexpected end of stream".into(),
108            Self::UnexpectedOpeningTag => "'<' is not allowed in attributes".into(),
109            Self::CannotUndefinePrefix(ref ln) => format!("Cannot undefine prefix '{ln}'").into(),
110            Self::ConflictingEncoding(a, b) => format!("Declared encoding {a}, but uses {b}").into(),
111            Self::InvalidCharacterEntity(num) => format!("Invalid character U+{num:04X}").into(),
112            Self::InvalidDefaultNamespace(ref name) => format!("Namespace '{name}' cannot be default").into(),
113            Self::InvalidNamePrefix(ref prefix) => format!("'{prefix}' cannot be an element name prefix").into(),
114            Self::InvalidNumericEntity(ref v) => format!("Invalid numeric entity: {v}").into(),
115            Self::InvalidQualifiedName(ref e) => format!("Qualified name is invalid: {e}").into(),
116            Self::InvalidStandaloneDeclaration(ref value) => format!("Invalid standalone declaration value: {value}").into(),
117            Self::InvalidXmlProcessingInstruction(ref name) => format!("Invalid processing instruction: <?{name}\nThe XML spec only allows \"<?xml\" at the very beginning of the file, with no whitespace, comments, or any elements before it").into(),
118            Self::RedefinedAttribute(ref name) => format!("Attribute '{name}' is redefined").into(),
119            Self::UnboundAttribute(ref name) => format!("Attribute {name} prefix is unbound").into(),
120            Self::UnboundElementPrefix(ref name) => format!("Element {name} prefix is unbound").into(),
121            Self::UndefinedEntity(ref v) => format!("Undefined entity: {v}").into(),
122            Self::UnexpectedClosingTag(ref expected_got) => format!("Unexpected closing tag: {expected_got}").into(),
123            Self::UnexpectedEntity(ref name) => format!("Unexpected entity: {name}").into(),
124            Self::UnexpectedName(ref name) => format!("Unexpected name: {name}").into(),
125            Self::UnexpectedNameInsideXml(ref name) => format!("Unexpected name inside XML declaration: {name}").into(),
126            Self::UnexpectedProcessingInstruction(ref buf, token) => format!("Unexpected token inside processing instruction: <?{buf}{token}").into(),
127            Self::UnexpectedQualifiedName(e) => format!("Unexpected token inside qualified name: {e}").into(),
128            Self::UnexpectedToken(token) => format!("Unexpected token: {token}").into(),
129            Self::UnexpectedTokenBefore(before, c) => format!("Unexpected token '{before}' before '{c}'").into(),
130            Self::UnexpectedTokenInClosingTag(token) => format!("Unexpected token inside closing tag: {token}").into(),
131            Self::UnexpectedTokenInEntity(token) => format!("Unexpected token inside entity: {token}").into(),
132            Self::UnexpectedTokenInOpeningTag(token) => format!("Unexpected token inside opening tag: {token}").into(),
133            Self::UnexpectedTokenOutsideRoot(token) => format!("Unexpected characters outside the root element: {token}").into(),
134            Self::UnexpectedXmlVersion(ref version) => format!("Invalid XML version: {version}").into(),
135            Self::UnknownMarkupDeclaration(ref v) => format!("Unknown markup declaration: {v}").into(),
136            Self::UnsupportedEncoding(ref v) => format!("Unsupported encoding: {v}").into(),
137            Self::ExceededConfiguredLimit => "This document is larger/more complex than allowed by the parser's configuration".into(),
138        }
139    }
140}
141
142/// An XML parsing error.
143///
144/// Consists of a 2D position in a document and a textual message describing the error.
145#[derive(Clone, PartialEq, Eq, Debug)]
146pub struct Error {
147    pub(crate) pos: TextPosition,
148    pub(crate) kind: ErrorKind,
149}
150
151impl fmt::Display for Error {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        use self::ErrorKind::{EmitterError, Io, Syntax, UnexpectedEof, Utf8};
154
155        write!(f, "{} ", self.pos)?;
156        match &self.kind {
157            Io(io_error) => io_error.fmt(f),
158            Utf8(reason) => reason.fmt(f),
159            Syntax(msg) => f.write_str(msg),
160            UnexpectedEof => f.write_str("Unexpected EOF"),
161            EmitterError(e) => e.fmt(f),
162        }
163    }
164}
165
166impl fmt::Display for ImmutableEntitiesError {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        f.write_str(match self {
169            Self::ElementEncountered => "Element encountered",
170            Self::StandaloneDocument => "Standalone XML",
171        })
172    }
173}
174
175impl fmt::Debug for ImmutableEntitiesError {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        fmt::Display::fmt(self, f)
178    }
179}
180
181impl error::Error for ImmutableEntitiesError {
182}
183
184impl Position for Error {
185    #[inline]
186    fn position(&self) -> TextPosition { self.pos }
187}
188
189impl Error {
190    #[doc(hidden)]
191    #[must_use]
192    pub fn msg(&self) -> String {
193        self.to_string()
194    }
195
196    /// Failure reason
197    #[must_use]
198    #[inline]
199    pub fn kind(&self) -> &ErrorKind {
200        &self.kind
201    }
202
203    pub(crate) fn syntax(syntax_msg: Cow<'static, str>, pos: TextPosition) -> Self {
204        Self {
205            kind: ErrorKind::Syntax(syntax_msg),
206            pos
207        }
208    }
209}
210
211impl error::Error for Error {
212    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
213        match &self.kind {
214            ErrorKind::Io(e) => e.source(),
215            ErrorKind::Utf8(e) => Some(e),
216            ErrorKind::EmitterError(e) => Some(e),
217            _ => None,
218        }
219    }
220}
221
222impl<'a, P, M> From<(&'a P, M)> for Error where P: Position, M: Into<Cow<'static, str>> {
223    #[cold]
224    fn from(orig: (&'a P, M)) -> Self {
225        Self {
226            pos: orig.0.position(),
227            kind: ErrorKind::Syntax(orig.1.into()),
228        }
229    }
230}
231
232impl From<util::CharReadError> for Error {
233    #[cold]
234    fn from(e: util::CharReadError) -> Self {
235        use crate::util::CharReadError::{Io, UnexpectedEof, Utf8};
236        Self {
237            pos: TextPosition::new(),
238            kind: match e {
239                UnexpectedEof => ErrorKind::UnexpectedEof,
240                Utf8(reason) => ErrorKind::Utf8(reason),
241                Io(io_error) => ErrorKind::Io(io_error),
242            },
243        }
244    }
245}
246
247impl From<io::Error> for Error {
248    #[cold]
249    fn from(e: io::Error) -> Self {
250        Self {
251            pos: TextPosition::new(),
252            kind: ErrorKind::Io(e),
253        }
254    }
255}
256
257impl From<EmitterError> for Error {
258    #[cold]
259    fn from(e: EmitterError) -> Self {
260        Self {
261            pos: TextPosition::new(),
262            kind: ErrorKind::EmitterError(Box::new(e)),
263        }
264    }
265}
266
267impl From<ImmutableEntitiesError> for Error {
268    #[cold]
269    fn from(e: ImmutableEntitiesError) -> Self {
270        Self {
271            pos: TextPosition::new(),
272            kind: ErrorKind::Io(io::Error::new(io::ErrorKind::Other, e)),
273        }
274    }
275}
276
277impl From<ErrorKind> for Error {
278    fn from(kind: ErrorKind) -> Self {
279        Self { kind, pos: TextPosition::new() }
280    }
281}
282
283impl Clone for ErrorKind {
284    #[cold]
285    fn clone(&self) -> Self {
286        use self::ErrorKind::{EmitterError, Io, Syntax, UnexpectedEof, Utf8};
287        match self {
288            UnexpectedEof => UnexpectedEof,
289            Utf8(reason) => Utf8(*reason),
290            Io(io_error) => Io(io::Error::new(io_error.kind(), io_error.to_string())),
291            Syntax(msg) => Syntax(msg.clone()),
292            EmitterError(e) => EmitterError(e.clone()),
293        }
294    }
295}
296
297impl PartialEq for ErrorKind {
298    #[allow(deprecated)]
299    fn eq(&self, other: &Self) -> bool {
300        use self::ErrorKind::{Io, Syntax, UnexpectedEof, Utf8};
301        match (self, other) {
302            (UnexpectedEof, UnexpectedEof) => true,
303            (Utf8(left), Utf8(right)) => left == right,
304            (Io(left), Io(right)) =>
305                left.kind() == right.kind() &&
306                left.description() == right.description(),
307            (Syntax(left), Syntax(right)) =>
308                left == right,
309            (_, _) => false,
310        }
311    }
312}
313impl Eq for ErrorKind {}
314
315#[test]
316fn err_size() {
317    assert!(std::mem::size_of::<SyntaxError>() <= 24);
318}