xot/
error.rs

1use crate::{xotdata::Node, Span};
2
3/// An error that occurred during parsing.
4#[derive(Debug, Clone)]
5pub enum ParseError {
6    /// The XML is not well-formed - a tag is opened and never closed.
7    UnclosedTag(Span),
8    /// The XML is not well-formed - a tag is closed that was never opened.
9    InvalidCloseTag(String, String, Span),
10    /// The XML is not well-formed - you use `&` to open an entity without
11    /// closing it with `;`.
12    UnclosedEntity(String, usize),
13    /// The entity is not known. Only the basic entities are supported
14    /// right now, not any user defined ones.
15    InvalidEntity(String, Span),
16    /// You used a namespace prefix that is not declared during parsing.
17    UnknownPrefix(String, Span),
18    /// You declared an attribute of the same name twice.
19    DuplicateAttribute(String, Span),
20    /// Unsupported XML version. Only 1.0 is supported.
21    UnsupportedVersion(String, Span),
22    /// Unsupported standalone declaration. This error is deprecated since version 0.29, and both
23    /// "yes" and "no" values are accepted for the standalone declaration.
24    #[deprecated(
25        since = "0.2.9",
26        note = "The value of the standalone declaration is now ignored"
27    )]
28    UnsupportedNotStandalone(Span),
29    /// XML DTD is not supported.
30    DtdUnsupported(Span),
31    /// No top-level element in the document.
32    NoElementAtTopLevel(usize),
33    /// Multiple top-level elements in the document.
34    MultipleElementsAtTopLevel(Span),
35    /// Text at top level is not allowed in a well-formed document.
36    TextAtTopLevel(Span),
37    /// Duplicate xml:id is not allowed
38    DuplicateId(String, Span),
39    /// xmlparser error
40    XmlParser(xmlparser::Error, usize),
41}
42
43impl ParseError {
44    /// Obtain the span for a ParseError.
45    pub fn span(&self) -> Span {
46        match self {
47            ParseError::UnclosedTag(span) => *span,
48            ParseError::InvalidCloseTag(_, _, span) => *span,
49            ParseError::UnclosedEntity(_, position) => Span::new(*position, *position),
50            ParseError::InvalidEntity(_, span) => *span,
51            ParseError::UnknownPrefix(_, span) => *span,
52            ParseError::DuplicateAttribute(_, span) => *span,
53            ParseError::UnsupportedVersion(_, span) => *span,
54            #[allow(deprecated)]
55            ParseError::UnsupportedNotStandalone(span) => *span,
56            ParseError::DtdUnsupported(span) => *span,
57            ParseError::NoElementAtTopLevel(position) => Span::new(*position, *position),
58            ParseError::MultipleElementsAtTopLevel(span) => *span,
59            ParseError::TextAtTopLevel(span) => *span,
60            ParseError::DuplicateId(_, span) => *span,
61            ParseError::XmlParser(_, position) => Span::new(*position, *position),
62        }
63    }
64}
65
66/// Xot errors
67#[derive(Debug, Clone)]
68pub enum Error {
69    // access errors
70    /// The node is not a Document node.
71    NotDocument(Node),
72
73    // manipulation errors
74    /// Invalid operation on XML. You get this when
75    /// trying to remove the document, or trying to
76    /// insert something under a text node, for instance.
77    InvalidOperation(String),
78
79    /// You aren't allowed to use this string as a comment.
80    /// Happens if you include `--` in a comment.
81    InvalidComment(String),
82    /// You aren't allowed to use this string as a processing instruction
83    /// target. Happens if you use `XML` or any case variation of this.
84    InvalidTarget(String),
85    /// The node you tried to act on is not an element.
86    NotElement(Node),
87    /// Indextree error that can happen during manipulation.
88    NodeError(indextree::NodeError),
89
90    // serializer
91    /// Missing prefix for namespace.
92    /// Can occur during serialization if a namespace is used that has no
93    /// prefix is declared. Use [`Xot::create_missing_prefixes`](crate::xotdata::Xot::create_missing_prefixes)
94    /// to fix this.
95    MissingPrefix(String),
96    /// It's not allowed to serialize a processing instruction to HTML with a > in it.
97    ProcessingInstructionGtInHtml(String),
98
99    /// It's not allowed to include a namespace prefix in a processing instruction
100    /// target name.
101    NamespaceInProcessingInstruction,
102
103    /// An error during parsing
104    Parse(ParseError),
105
106    /// You used a namespace prefix that is not declared.
107    ///
108    /// Note that this error does not occur during parsing but during
109    /// name creation.
110    UnknownPrefix(String),
111
112    /// Illegal content that can never appear under a document node, such as an
113    /// attribute or a namespace node
114    IllegalAtTopLevel(Node),
115    /// A text node at top level is not allowed in a well formed document,
116    /// but it is accepted as a fragment.
117    TextAtTopLevel(Node),
118    /// Missing document element at top level
119    NoElementAtTopLevel,
120    /// Multiple document elements at top level
121    MultipleElementsAtTopLevel,
122
123    /// IO error
124    ///
125    /// We take the string version of the IO error so as to keep errors comparable,
126    /// which is more important than the exact error object in this case (serialization)
127    Io(String),
128}
129
130impl From<indextree::NodeError> for Error {
131    #[inline]
132    fn from(e: indextree::NodeError) -> Self {
133        Error::NodeError(e)
134    }
135}
136
137impl From<std::io::Error> for Error {
138    #[inline]
139    fn from(e: std::io::Error) -> Self {
140        Error::Io(e.to_string())
141    }
142}
143
144impl From<ParseError> for Error {
145    #[inline]
146    fn from(e: ParseError) -> Self {
147        Error::Parse(e)
148    }
149}
150
151impl std::fmt::Display for Error {
152    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
153        match self {
154            Error::NotDocument(_) => write!(f, "Not a document node"),
155            Error::InvalidOperation(s) => write!(f, "Invalid operation: {}", s),
156            Error::InvalidComment(s) => write!(f, "Invalid comment: {}", s),
157            Error::InvalidTarget(s) => write!(f, "Invalid target: {}", s),
158            Error::NotElement(_) => write!(f, "Not an element"),
159            Error::NodeError(e) => write!(f, "Node error: {}", e),
160            Error::MissingPrefix(_) => write!(f, "Missing prefix"),
161            Error::ProcessingInstructionGtInHtml(s) => {
162                write!(f, "Processing instruction with > in HTML: {}", s)
163            }
164            Error::NamespaceInProcessingInstruction => {
165                write!(f, "Namespace in processing instruction target")
166            }
167            Error::Parse(e) => write!(f, "Parse error: {:?}", e),
168            Error::UnknownPrefix(s) => write!(f, "Unknown prefix: {}", s),
169            Error::IllegalAtTopLevel(_) => write!(f, "Illegal content under document node (attribute, namespace or document node"),
170            Error::TextAtTopLevel(_) => write!(f, "Text node under document not. Not allowed in a well-formed document, but allowed in a fragment"),
171            Error::NoElementAtTopLevel => write!(f, "No element under document root. Not allowed in a well-formed document, but allowed in a fragment"),
172            Error::MultipleElementsAtTopLevel => write!(f, "Multiple elements under document root. Not allowed in a well-formed document, but allowed in a fragment"),
173            Error::Io(s) => write!(f, "IO error: {}", s),
174        }
175    }
176}
177
178impl std::fmt::Display for ParseError {
179    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
180        match self {
181            ParseError::UnclosedTag(_) => write!(f, "Unclosed tag"),
182            ParseError::InvalidCloseTag(s, s2, _) => write!(f, "Invalid close tag: {} {}", s, s2),
183            ParseError::UnclosedEntity(s, _) => write!(f, "Unclosed entity: {}", s),
184            ParseError::InvalidEntity(s, _) => write!(f, "Invalid entity: {}", s),
185            ParseError::UnknownPrefix(s, _) => write!(f, "Unknown prefix: {}", s),
186            ParseError::DuplicateAttribute(s, _) => write!(f, "Duplicate attribute: {}", s),
187            ParseError::UnsupportedVersion(s, _) => write!(f, "Unsupported version: {}", s),
188            #[allow(deprecated)]
189            ParseError::UnsupportedNotStandalone(_) => write!(f, "Unsupported standalone"),
190            ParseError::DtdUnsupported(_) => write!(f, "DTD is not supported"),
191            ParseError::NoElementAtTopLevel(_) => write!(f, "No element at top level"),
192            ParseError::MultipleElementsAtTopLevel(_) => {
193                write!(f, "Multiple elements at top level")
194            }
195            ParseError::TextAtTopLevel(_) => write!(f, "Text at top level"),
196            ParseError::DuplicateId(s, _) => write!(f, "Duplicate xml:id: {}", s),
197            ParseError::XmlParser(e, _position) => write!(f, "Parser error: {}", e),
198        }
199    }
200}
201
202impl std::error::Error for ParseError {
203    fn description(&self) -> &str {
204        "XML parsing error"
205    }
206}
207
208impl std::error::Error for Error {
209    fn description(&self) -> &str {
210        "Xot error"
211    }
212}