htmlite/
err.rs

1use std::error::Error as StdErr;
2use std::fmt::Display;
3
4/// An error that occurs while tokenizing or parsing a chunk of html
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct Err {
7    #[allow(missing_docs)]
8    pub kind: ErrKind,
9    /// Byte offset into the html string where the error occured
10    ///
11    /// Usage errors won't have an offset.
12    pub offset: Option<usize>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16#[allow(missing_docs)]
17pub enum ErrKind {
18    // Parser errors
19    BadlyNestedEndTag,
20    EndTagWithoutCorrespondingStartTag,
21    UnclosedStartTag,
22    DoctypeInForeignElement,
23    VoidElementAsEndTag,
24    NonVoidElementStartTagWithTrailingSolidus,
25    // Tokenizer errors
26    InvalidUtf8,
27    InvalidFirstCharacterOfTagName,
28    InvalidCharacterSequenceAfterDoctypeName,
29    UnexpectedNullCharacter,
30    UnexpectedEqualsBeforeAttributeName,
31    UnexpectedCharacterInAttributeName,
32    UnexpectedQuestionMarkInsteadOfTagName,
33    UnexpectedCharacterInUnquotedAttributeValue,
34    UnexpectedSolidusInTag,
35    MissingSemiColonAfterCharacterReference,
36    MissingEndTagName,
37    MissingAttributeValue,
38    MissingWhitespaceBeforeDoctypeName,
39    MissingDoctypeName,
40    MissingWhitespaceBetweenAttributes,
41    UnknownCharacterReference,
42    EofBeforeTagName,
43    EofInTag,
44    EofInDoctype,
45    EofInComment,
46    IncorrectlyOpenedComment,
47    EndTagWithTrailingSolidus,
48    EndTagWithAttributes,
49    DuplicateAttribute,
50    NullCharacterReference,
51    CharacterReferenceOutsideUnicodeRange,
52    SurrogateCharacterReference,
53    NonCharacterCharacterReference,
54    ControlCharacterReference,
55    // Tree Manipulation errors
56    CannotAppendToNode,
57    CannotInsertAtRoot,
58    CannotReplaceRoot,
59}
60
61impl StdErr for Err {}
62
63impl Display for Err {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        let msg = match &self.kind {
66            // Tokenizer errors
67            ErrKind::InvalidUtf8 => "Invalid utf8 bytes sequence",
68            ErrKind::InvalidFirstCharacterOfTagName => {
69                "The first character of a tag must be an ASCII alphabetic character (invalid-first-character-of-tag-name)"
70            }
71            ErrKind::InvalidCharacterSequenceAfterDoctypeName => {
72                "No text is permitted in the DOCTYPE tag after the name (invalid-character-sequence-after-doctype-name)"
73            }
74            ErrKind::UnexpectedNullCharacter => {
75                "Null byte is not allowed here (unexpected-null-character)"
76            }
77            ErrKind::UnexpectedEqualsBeforeAttributeName => {
78                "Saw an equal sign before an attribute name (unexpected-equals-sign-before-attribute-name)"
79            }
80            ErrKind::UnexpectedCharacterInAttributeName => {
81                "Saw a `<`, `'` or `\"` in an attribute name (unexpected-character-in-attribute-name)"
82            }
83            ErrKind::UnexpectedQuestionMarkInsteadOfTagName => {
84                "Saw question mark instead of tag name. Is this perhaps an XML declaration or processing instruction? (e.g. <?xml version>) (unexpected-question-mark-instead-of-tag-name)"
85            }
86            ErrKind::UnexpectedCharacterInUnquotedAttributeValue => {
87                "Saw a (\"), ('), (<), (=) or (`) in an unquoted attribute vaule (unexpected-character-in-unquoted-attribute-value)"
88            }
89            ErrKind::UnexpectedSolidusInTag => {
90                "Saw a stray (\\) in a tag (unexpected-solidus-in-tag)"
91            }
92            ErrKind::MissingSemiColonAfterCharacterReference => {
93                "Saw what looks like a character reference, but the semi-colon was missing (missing-semicolon-after-character-reference)"
94            }
95            ErrKind::MissingEndTagName => {
96                "End tag must have a name. So this `</>` is not allowed (missing-end-tag-name)"
97            }
98            ErrKind::MissingAttributeValue => {
99                "Expected an attribute value after the equals sign (missing-attribute-value)"
100            }
101            ErrKind::MissingWhitespaceBeforeDoctypeName => {
102                "Doctype tag must have whitespace after the 'DOCTYPE' literal (missing-whitespace-before-doctype-name)"
103            }
104            ErrKind::MissingDoctypeName => {
105                "Doctype must have a name. So this `<!doctype>` is not allowed (missing-doctype-name)"
106            }
107            ErrKind::MissingWhitespaceBetweenAttributes => {
108                "Attributes must be seperated by whitespace (missing-whitespace-between-attributes)"
109            }
110            ErrKind::UnknownCharacterReference => "Failed to resolve character reference",
111            ErrKind::EofBeforeTagName => "Incomplete tag (eof-before-tag-name)",
112            ErrKind::EofInTag => "Incomplete tag (eof-in-tag)",
113            ErrKind::EofInDoctype => "Incomplete doctype declaration (eof-in-doctype)",
114            ErrKind::EofInComment => "Incomplete comment (eof-in-comment)",
115            ErrKind::IncorrectlyOpenedComment => "Comments must be started with (<!--)",
116            ErrKind::EndTagWithTrailingSolidus => {
117                "An end tag may not be self-closing (end-tag-with-trailing-solidus)"
118            }
119            ErrKind::EndTagWithAttributes => {
120                "An end tag may not have attributes (end-tag-with-attributes)"
121            }
122            ErrKind::DuplicateAttribute => {
123                "A tag may not have duplicate attributes (duplicate-attribute)"
124            }
125            ErrKind::NullCharacterReference => {
126                "Saw a character reference whose codepoint resolved to the null byte (null-character-reference)"
127            }
128            ErrKind::CharacterReferenceOutsideUnicodeRange => {
129                "Saw a character reference whose codepoint is outside the unicode range (character-reference-outside-unicode-range)"
130            }
131            ErrKind::SurrogateCharacterReference => {
132                "Saw a character reference whose codepoint resolved to a unicode surrogate (surrogate-character-reference)"
133            }
134            ErrKind::NonCharacterCharacterReference => {
135                "Saw a character reference whose codepoint resolved to a unicode noncharacter (noncharacter-character-reference)"
136            }
137            ErrKind::ControlCharacterReference => {
138                "Saw a character reference whose codepoint resolved to a non whitespace control character (control-character-reference)"
139            }
140            // Parser errors
141            ErrKind::EndTagWithoutCorrespondingStartTag => {
142                "Saw an end tag with no matching start tag"
143            }
144            ErrKind::BadlyNestedEndTag => {
145                "Badly nested end tag. Ensure the corresponding start is correctly nested"
146            }
147            ErrKind::UnclosedStartTag => "Start tag was not closed",
148            ErrKind::DoctypeInForeignElement => {
149                "Doctype declarations are not allowed in foreign namespaces"
150            }
151            ErrKind::VoidElementAsEndTag => "Void element may not appear as an end tag",
152            ErrKind::NonVoidElementStartTagWithTrailingSolidus => {
153                "Only void html elements may be self-closing"
154            }
155            ErrKind::CannotAppendToNode => "Only root & element nodes may be appending to",
156            ErrKind::CannotInsertAtRoot => "Cannot insert elements before or after the root node",
157            ErrKind::CannotReplaceRoot => "Root element cannot be replaced",
158        };
159        if let Some(offset) = self.offset {
160            write!(f, "Parsing error at byte offset {offset}: {msg}")
161        } else {
162            write!(f, "Error: {msg}")
163        }
164    }
165}