1use ecow::EcoString;
7
8use crate::writer::html::error::HtmlWriteError as CoreHtmlWriteError;
9use std::error::Error;
10use std::fmt::{self, Display};
11use std::io;
12
13#[derive(Debug)]
15pub enum WriteError {
16 InvalidHeadingLevel(u8),
18 NewlineInInlineElement(EcoString),
20 FmtError(EcoString),
22 IoError(io::Error),
24 UnsupportedNodeType,
26 InvalidStructure(EcoString),
28 InvalidHtmlTag(EcoString),
30 InvalidHtmlAttribute(EcoString),
32 HtmlRenderingError(CoreHtmlWriteError),
34 HtmlFallbackError(EcoString),
36 Custom {
38 message: EcoString,
40 code: Option<EcoString>,
42 },
43}
44
45impl Display for WriteError {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 WriteError::InvalidHeadingLevel(level) => write!(
49 f,
50 "Invalid heading level: {level}. Level must be between 1 and 6."
51 ),
52 WriteError::NewlineInInlineElement(context) => write!(
53 f,
54 "Newline character found within an inline element ({context}) which is not allowed in strict mode or this context."
55 ),
56 WriteError::FmtError(msg) => write!(f, "Formatting error: {msg}"),
57 WriteError::IoError(err) => write!(f, "I/O error: {err}"),
58 WriteError::UnsupportedNodeType => {
59 write!(f, "Unsupported node type encountered during writing.")
60 },
61 WriteError::InvalidStructure(msg) => {
62 write!(f, "Invalid structure: {msg}")
63 },
64 WriteError::InvalidHtmlTag(tag) => {
65 write!(f, "Invalid HTML tag name: '{tag}'. Tag names should only contain alphanumeric characters, underscores, colons, or hyphens.")
66 },
67 WriteError::InvalidHtmlAttribute(attr) => {
68 write!(f, "Invalid HTML attribute name: '{attr}'. Attribute names should only contain alphanumeric characters, underscores, colons, dots, or hyphens.")
69 },
70 WriteError::HtmlRenderingError(html_err) => {
71 write!(f, "Error during HTML rendering phase: {html_err}")
72 },
73 WriteError::HtmlFallbackError(msg) => {
74 write!(f, "Error during HTML fallback rendering: {msg}")
75 },
76 WriteError::Custom { message, code } => {
77 if let Some(code) = code {
78 write!(f, "Custom error [{code}]: {message}")
79 } else {
80 write!(f, "Custom error: {message}")
81 }
82 }
83 }
84 }
85}
86
87impl Error for WriteError {}
88
89impl From<fmt::Error> for WriteError {
91 fn from(err: fmt::Error) -> Self {
92 WriteError::FmtError(err.to_string().into())
93 }
94}
95
96impl From<io::Error> for WriteError {
98 fn from(err: io::Error) -> Self {
99 WriteError::IoError(err)
100 }
101}
102
103impl From<CoreHtmlWriteError> for WriteError {
105 fn from(err: CoreHtmlWriteError) -> Self {
106 match err {
107 CoreHtmlWriteError::InvalidHtmlTag(tag) => WriteError::InvalidHtmlTag(tag.into()),
108 CoreHtmlWriteError::InvalidHtmlAttribute(attr) => {
109 WriteError::InvalidHtmlAttribute(attr.into())
110 }
111 other_html_err => WriteError::HtmlRenderingError(other_html_err),
112 }
113 }
114}
115
116pub type WriteResult<T> = Result<T, WriteError>;
118
119impl WriteError {
121 pub fn custom<S: Into<EcoString>>(message: S) -> Self {
123 WriteError::Custom {
124 message: message.into(),
125 code: None,
126 }
127 }
128
129 pub fn custom_with_code<S1: Into<EcoString>, S2: Into<EcoString>>(
131 message: S1,
132 code: S2,
133 ) -> Self {
134 WriteError::Custom {
135 message: message.into(),
136 code: Some(code.into()),
137 }
138 }
139}
140
141pub trait CustomErrorFactory {
146 fn create_error(&self) -> WriteError;
148}
149
150pub struct StructureError {
152 format: EcoString,
154 args: Vec<EcoString>,
156}
157
158impl StructureError {
159 pub fn new<S: Into<EcoString>>(format: S) -> Self {
161 Self {
162 format: format.into(),
163 args: Vec::new(),
164 }
165 }
166
167 pub fn arg<S: Into<EcoString>>(mut self, arg: S) -> Self {
169 self.args.push(arg.into());
170 self
171 }
172}
173
174impl CustomErrorFactory for StructureError {
175 fn create_error(&self) -> WriteError {
176 let message = match self.args.len() {
177 0 => self.format.clone(),
178 1 => self.format.replace("{}", &self.args[0]),
179 _ => {
180 let mut result = self.format.to_string();
181 for arg in &self.args {
182 if let Some(pos) = result.find("{}") {
183 result.replace_range(pos..pos + 2, arg);
184 }
185 }
186 EcoString::from(result)
187 }
188 };
189
190 WriteError::InvalidStructure(message)
191 }
192}
193
194pub struct CodedError {
196 message: EcoString,
198 code: EcoString,
200}
201
202impl CodedError {
203 pub fn new<S1: Into<EcoString>, S2: Into<EcoString>>(message: S1, code: S2) -> Self {
205 Self {
206 message: message.into(),
207 code: code.into(),
208 }
209 }
210}
211
212impl CustomErrorFactory for CodedError {
213 fn create_error(&self) -> WriteError {
214 WriteError::custom_with_code(&self.message, &self.code)
215 }
216}
217
218pub trait WriteResultExt<T> {
220 fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError>;
222}
223
224impl<T> WriteResultExt<T> for Result<T, WriteError> {
225 fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError> {
226 Err(factory.create_error())
227 }
228}