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 must be between 1 and 6.",
51 level
52 ),
53 WriteError::NewlineInInlineElement(context) => write!(
54 f,
55 "Newline character found within an inline element ({}) which is not allowed in strict mode or this context.",
56 context
57 ),
58 WriteError::FmtError(msg) => write!(f, "Formatting error: {}", msg),
59 WriteError::IoError(err) => write!(f, "I/O error: {}", err),
60 WriteError::UnsupportedNodeType => {
61 write!(f, "Unsupported node type encountered during writing.")
62 },
63 WriteError::InvalidStructure(msg) => {
64 write!(f, "Invalid structure: {}", msg)
65 },
66 WriteError::InvalidHtmlTag(tag) => {
67 write!(f, "Invalid HTML tag name: '{}'. Tag names should only contain alphanumeric characters, underscores, colons, or hyphens.", tag)
68 },
69 WriteError::InvalidHtmlAttribute(attr) => {
70 write!(f, "Invalid HTML attribute name: '{}'. Attribute names should only contain alphanumeric characters, underscores, colons, dots, or hyphens.", attr)
71 },
72 WriteError::HtmlRenderingError(html_err) => {
73 write!(f, "Error during HTML rendering phase: {}", html_err)
74 },
75 WriteError::HtmlFallbackError(msg) => {
76 write!(f, "Error during HTML fallback rendering: {}", msg)
77 },
78 WriteError::Custom { message, code } => {
79 if let Some(code) = code {
80 write!(f, "Custom error [{}]: {}", code, message)
81 } else {
82 write!(f, "Custom error: {}", message)
83 }
84 }
85 }
86 }
87}
88
89impl Error for WriteError {}
90
91impl From<fmt::Error> for WriteError {
93 fn from(err: fmt::Error) -> Self {
94 WriteError::FmtError(err.to_string().into())
95 }
96}
97
98impl From<io::Error> for WriteError {
100 fn from(err: io::Error) -> Self {
101 WriteError::IoError(err)
102 }
103}
104
105impl From<CoreHtmlWriteError> for WriteError {
107 fn from(err: CoreHtmlWriteError) -> Self {
108 match err {
109 CoreHtmlWriteError::InvalidHtmlTag(tag) => WriteError::InvalidHtmlTag(tag.into()),
110 CoreHtmlWriteError::InvalidHtmlAttribute(attr) => {
111 WriteError::InvalidHtmlAttribute(attr.into())
112 }
113 other_html_err => WriteError::HtmlRenderingError(other_html_err),
114 }
115 }
116}
117
118pub type WriteResult<T> = Result<T, WriteError>;
120
121impl WriteError {
123 pub fn custom<S: Into<EcoString>>(message: S) -> Self {
125 WriteError::Custom {
126 message: message.into(),
127 code: None,
128 }
129 }
130
131 pub fn custom_with_code<S1: Into<EcoString>, S2: Into<EcoString>>(
133 message: S1,
134 code: S2,
135 ) -> Self {
136 WriteError::Custom {
137 message: message.into(),
138 code: Some(code.into()),
139 }
140 }
141}
142
143pub trait CustomErrorFactory {
148 fn create_error(&self) -> WriteError;
150}
151
152pub struct StructureError {
154 format: EcoString,
156 args: Vec<EcoString>,
158}
159
160impl StructureError {
161 pub fn new<S: Into<EcoString>>(format: S) -> Self {
163 Self {
164 format: format.into(),
165 args: Vec::new(),
166 }
167 }
168
169 pub fn arg<S: Into<EcoString>>(mut self, arg: S) -> Self {
171 self.args.push(arg.into());
172 self
173 }
174}
175
176impl CustomErrorFactory for StructureError {
177 fn create_error(&self) -> WriteError {
178 let message = match self.args.len() {
179 0 => self.format.clone(),
180 1 => self.format.replace("{}", &self.args[0]),
181 _ => {
182 let mut result = self.format.to_string();
183 for arg in &self.args {
184 if let Some(pos) = result.find("{}") {
185 result.replace_range(pos..pos + 2, arg);
186 }
187 }
188 EcoString::from(result)
189 }
190 };
191
192 WriteError::InvalidStructure(message)
193 }
194}
195
196pub struct CodedError {
198 message: EcoString,
200 code: EcoString,
202}
203
204impl CodedError {
205 pub fn new<S1: Into<EcoString>, S2: Into<EcoString>>(message: S1, code: S2) -> Self {
207 Self {
208 message: message.into(),
209 code: code.into(),
210 }
211 }
212}
213
214impl CustomErrorFactory for CodedError {
215 fn create_error(&self) -> WriteError {
216 WriteError::custom_with_code(&self.message, &self.code)
217 }
218}
219
220pub trait WriteResultExt<T> {
222 fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError>;
224}
225
226impl<T> WriteResultExt<T> for Result<T, WriteError> {
227 fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError> {
228 Err(factory.create_error())
229 }
230}