1use std::error::Error as StdErr;
2use std::fmt::Display;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct Err {
7 #[allow(missing_docs)]
8 pub kind: ErrKind,
9 pub offset: Option<usize>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16#[allow(missing_docs)]
17pub enum ErrKind {
18 BadlyNestedEndTag,
20 EndTagWithoutCorrespondingStartTag,
21 UnclosedStartTag,
22 DoctypeInForeignElement,
23 VoidElementAsEndTag,
24 NonVoidElementStartTagWithTrailingSolidus,
25 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 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 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 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}