xml_no_std/reader/
error.rs

1extern crate alloc;
2
3use crate::Encoding;
4use crate::reader::lexer::Token;
5
6use alloc::borrow::Cow;
7use alloc::boxed::Box;
8use alloc::string::String;
9
10use core::fmt;
11use core::str;
12
13use crate::common::{Position, TextPosition};
14use crate::util;
15
16/// Failure reason
17#[derive(Debug)]
18pub enum ErrorKind {
19    /// This is an ill-formed XML document
20    Syntax(Cow<'static, str>),
21    Io(String),
22    Utf8(str::Utf8Error),
23    /// The document ended while they were elements/comments/etc. still open
24    UnexpectedEof,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28#[non_exhaustive]
29pub(crate) enum SyntaxError {
30    CannotRedefineXmlnsPrefix,
31    CannotRedefineXmlPrefix,
32    /// Recursive custom entity expanded to too many chars, it could be DoS
33    EntityTooBig,
34    EmptyEntity,
35    NoRootElement,
36    ProcessingInstructionWithoutName,
37    UnbalancedRootElement,
38    UnexpectedEof,
39    UnexpectedOpeningTag,
40    /// Missing `]]>`
41    UnclosedCdata,
42    UnexpectedQualifiedName(Token),
43    UnexpectedTokenOutsideRoot(Token),
44    UnexpectedToken(Token),
45    UnexpectedTokenInEntity(Token),
46    UnexpectedTokenInClosingTag(Token),
47    UnexpectedTokenInOpeningTag(Token),
48    InvalidQualifiedName(Box<str>),
49    UnboundAttribute(Box<str>),
50    UnboundElementPrefix(Box<str>),
51    UnexpectedClosingTag(Box<str>),
52    UnexpectedName(Box<str>),
53    /// Found <?xml-like PI not at the beginning of a document,
54    /// which is an error, see section 2.6 of XML 1.1 spec
55    UnexpectedProcessingInstruction(Box<str>, Token),
56    CannotUndefinePrefix(Box<str>),
57    InvalidCharacterEntity(u32),
58    InvalidDefaultNamespace(Box<str>),
59    InvalidNamePrefix(Box<str>),
60    InvalidNumericEntity(Box<str>),
61    InvalidStandaloneDeclaration(Box<str>),
62    InvalidXmlProcessingInstruction(Box<str>),
63    RedefinedAttribute(Box<str>),
64    UndefinedEntity(Box<str>),
65    UnexpectedEntity(Box<str>),
66    UnexpectedNameInsideXml(Box<str>),
67    UnsupportedEncoding(Box<str>),
68    /// In DTD
69    UnknownMarkupDeclaration(Box<str>),
70    UnexpectedXmlVersion(Box<str>),
71    ConflictingEncoding(Encoding, Encoding),
72    UnexpectedTokenBefore(&'static str, char),
73    /// Document has more stuff than `ParserConfig` allows
74    ExceededConfiguredLimit,
75}
76
77impl fmt::Display for SyntaxError {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        self.to_cow().fmt(f)
80    }
81}
82
83impl SyntaxError {
84    #[inline(never)]
85    #[cold]
86    pub(crate) fn to_cow(&self) -> Cow<'static, str> {
87        match *self {
88            Self::CannotRedefineXmlnsPrefix => "Cannot redefine XMLNS prefix".into(),
89            Self::CannotRedefineXmlPrefix => "Default XMLNS prefix cannot be rebound to another value".into(),
90            Self::EmptyEntity => "Encountered empty entity".into(),
91            Self::EntityTooBig => "Entity too big".into(),
92            Self::NoRootElement => "Unexpected end of stream: no root element found".into(),
93            Self::ProcessingInstructionWithoutName => "Encountered processing instruction without a name".into(),
94            Self::UnbalancedRootElement => "Unexpected end of stream: still inside the root element".into(),
95            Self::UnclosedCdata => "Unclosed <![CDATA[".into(),
96            Self::UnexpectedEof => "Unexpected end of stream".into(),
97            Self::UnexpectedOpeningTag => "'<' is not allowed in attributes".into(),
98            Self::CannotUndefinePrefix(ref ln) => alloc::format!("Cannot undefine prefix '{ln}'").into(),
99            Self::ConflictingEncoding(a, b) => alloc::format!("Declared encoding {a}, but uses {b}").into(),
100            Self::InvalidCharacterEntity(num) => alloc::format!("Invalid character U+{num:04X}").into(),
101            Self::InvalidDefaultNamespace(ref name) => alloc::format!("Namespace '{name}' cannot be default").into(),
102            Self::InvalidNamePrefix(ref prefix) => alloc::format!("'{prefix}' cannot be an element name prefix").into(),
103            Self::InvalidNumericEntity(ref v) => alloc::format!("Invalid numeric entity: {v}").into(),
104            Self::InvalidQualifiedName(ref e) => alloc::format!("Qualified name is invalid: {e}").into(),
105            Self::InvalidStandaloneDeclaration(ref value) => alloc::format!("Invalid standalone declaration value: {value}").into(),
106            Self::InvalidXmlProcessingInstruction(ref name) => alloc::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(),
107            Self::RedefinedAttribute(ref name) => alloc::format!("Attribute '{name}' is redefined").into(),
108            Self::UnboundAttribute(ref name) => alloc::format!("Attribute {name} prefix is unbound").into(),
109            Self::UnboundElementPrefix(ref name) => alloc::format!("Element {name} prefix is unbound").into(),
110            Self::UndefinedEntity(ref v) => alloc::format!("Undefined entity: {v}").into(),
111            Self::UnexpectedClosingTag(ref expected_got) => alloc::format!("Unexpected closing tag: {expected_got}").into(),
112            Self::UnexpectedEntity(ref name) => alloc::format!("Unexpected entity: {name}").into(),
113            Self::UnexpectedName(ref name) => alloc::format!("Unexpected name: {name}").into(),
114            Self::UnexpectedNameInsideXml(ref name) => alloc::format!("Unexpected name inside XML declaration: {name}").into(),
115            Self::UnexpectedProcessingInstruction(ref buf, token) => alloc::format!("Unexpected token inside processing instruction: <?{buf}{token}").into(),
116            Self::UnexpectedQualifiedName(e) => alloc::format!("Unexpected token inside qualified name: {e}").into(),
117            Self::UnexpectedToken(token) => alloc::format!("Unexpected token: {token}").into(),
118            Self::UnexpectedTokenBefore(before, c) => alloc::format!("Unexpected token '{before}' before '{c}'").into(),
119            Self::UnexpectedTokenInClosingTag(token) => alloc::format!("Unexpected token inside closing tag: {token}").into(),
120            Self::UnexpectedTokenInEntity(token) => alloc::format!("Unexpected token inside entity: {token}").into(),
121            Self::UnexpectedTokenInOpeningTag(token) => alloc::format!("Unexpected token inside opening tag: {token}").into(),
122            Self::UnexpectedTokenOutsideRoot(token) => alloc::format!("Unexpected characters outside the root element: {token}").into(),
123            Self::UnexpectedXmlVersion(ref version) => alloc::format!("Invalid XML version: {version}").into(),
124            Self::UnknownMarkupDeclaration(ref v) => alloc::format!("Unknown markup declaration: {v}").into(),
125            Self::UnsupportedEncoding(ref v) => alloc::format!("Unsupported encoding: {v}").into(),
126            Self::ExceededConfiguredLimit => "This document is larger/more complex than allowed by the parser's configuration".into(),
127        }
128    }
129}
130
131/// An XML parsing error.
132///
133/// Consists of a 2D position in a document and a textual message describing the error.
134#[derive(Clone, PartialEq, Eq, Debug)]
135pub struct Error {
136    pub(crate) pos: TextPosition,
137    pub(crate) kind: ErrorKind,
138}
139
140impl fmt::Display for Error {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        use self::ErrorKind::{Io, Syntax, UnexpectedEof, Utf8};
143
144        write!(f, "{} ", self.pos)?;
145        match &self.kind {
146            Io(io_error) => io_error.fmt(f),
147            Utf8(reason) => reason.fmt(f),
148            Syntax(msg) => f.write_str(msg),
149            UnexpectedEof => f.write_str("Unexpected EOF"),
150        }
151    }
152}
153
154impl Position for Error {
155    #[inline]
156    fn position(&self) -> TextPosition { self.pos }
157}
158
159impl Error {
160    /// Returns a reference to a message which is contained inside this error.
161    #[cold]
162    #[doc(hidden)]
163    #[allow(deprecated)]
164    #[must_use] pub fn msg(&self) -> &str {
165        use self::ErrorKind::{Io, Syntax, UnexpectedEof, Utf8};
166        match &self.kind {
167            Io(io_error) => &io_error,
168            Utf8(_) => "UTF8 Error",
169            Syntax(msg) => msg.as_ref(),
170            UnexpectedEof => "Unexpected EOF",
171        }
172    }
173
174    /// Failure reason
175    #[must_use]
176    #[inline]
177    pub fn kind(&self) -> &ErrorKind {
178        &self.kind
179    }
180}
181
182impl<'a, P, M> From<(&'a P, M)> for Error where P: Position, M: Into<Cow<'static, str>> {
183    #[cold]
184    fn from(orig: (&'a P, M)) -> Self {
185        Error {
186            pos: orig.0.position(),
187            kind: ErrorKind::Syntax(orig.1.into()),
188        }
189    }
190}
191
192impl From<util::CharReadError> for Error {
193    #[cold]
194    fn from(e: util::CharReadError) -> Self {
195        use crate::util::CharReadError::{Io, UnexpectedEof, Utf8};
196        Error {
197            pos: TextPosition::new(),
198            kind: match e {
199                UnexpectedEof => ErrorKind::UnexpectedEof,
200                Utf8(reason) => ErrorKind::Utf8(reason),
201                Io(io_error) => ErrorKind::Io(io_error),
202            },
203        }
204    }
205}
206
207
208impl Clone for ErrorKind {
209    #[cold]
210    fn clone(&self) -> Self {
211        use self::ErrorKind::{Io, Syntax, UnexpectedEof, Utf8};
212        match self {
213            UnexpectedEof => UnexpectedEof,
214            Utf8(reason) => Utf8(*reason),
215            Io(io_error) => Io(io_error.clone()),
216            Syntax(msg) => Syntax(msg.clone()),
217        }
218    }
219}
220impl PartialEq for ErrorKind {
221    #[allow(deprecated)]
222    fn eq(&self, other: &ErrorKind) -> bool {
223        use self::ErrorKind::{Io, Syntax, UnexpectedEof, Utf8};
224        match (self, other) {
225            (UnexpectedEof, UnexpectedEof) => true,
226            (Utf8(left), Utf8(right)) => left == right,
227            (Io(left), Io(right)) =>
228                left == right,
229            (Syntax(left), Syntax(right)) =>
230                left == right,
231
232            (_, _) => false,
233        }
234    }
235}
236impl Eq for ErrorKind {}
237
238#[test]
239fn err_size() {
240    assert!(std::mem::size_of::<SyntaxError>() <= 24);
241}