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 Custom {
36 message: EcoString,
38 code: Option<EcoString>,
40 },
41}
42
43impl Display for WriteError {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 WriteError::InvalidHeadingLevel(level) => write!(
47 f,
48 "Invalid heading level: {}. Level must be between 1 and 6.",
49 level
50 ),
51 WriteError::NewlineInInlineElement(context) => write!(
52 f,
53 "Newline character found within an inline element ({}) which is not allowed in strict mode or this context.",
54 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 names should only contain alphanumeric characters, underscores, colons, or hyphens.", tag)
66 },
67 WriteError::InvalidHtmlAttribute(attr) => {
68 write!(f, "Invalid HTML attribute name: '{}'. Attribute names should only contain alphanumeric characters, underscores, colons, dots, or hyphens.", attr)
69 },
70 WriteError::HtmlRenderingError(html_err) => {
71 write!(f, "Error during HTML rendering phase: {}", html_err)
72 },
73 WriteError::Custom { message, code } => {
74 if let Some(code) = code {
75 write!(f, "Custom error [{}]: {}", code, message)
76 } else {
77 write!(f, "Custom error: {}", message)
78 }
79 }
80 }
81 }
82}
83
84impl Error for WriteError {}
85
86impl From<fmt::Error> for WriteError {
88 fn from(err: fmt::Error) -> Self {
89 WriteError::FmtError(err.to_string().into())
90 }
91}
92
93impl From<io::Error> for WriteError {
95 fn from(err: io::Error) -> Self {
96 WriteError::IoError(err)
97 }
98}
99
100impl From<CoreHtmlWriteError> for WriteError {
102 fn from(err: CoreHtmlWriteError) -> Self {
103 match err {
104 CoreHtmlWriteError::InvalidHtmlTag(tag) => WriteError::InvalidHtmlTag(tag.into()),
105 CoreHtmlWriteError::InvalidHtmlAttribute(attr) => {
106 WriteError::InvalidHtmlAttribute(attr.into())
107 }
108 other_html_err => WriteError::HtmlRenderingError(other_html_err),
109 }
110 }
111}
112
113pub type WriteResult<T> = Result<T, WriteError>;
115
116impl WriteError {
118 pub fn custom<S: Into<EcoString>>(message: S) -> Self {
120 WriteError::Custom {
121 message: message.into(),
122 code: None,
123 }
124 }
125
126 pub fn custom_with_code<S1: Into<EcoString>, S2: Into<EcoString>>(
128 message: S1,
129 code: S2,
130 ) -> Self {
131 WriteError::Custom {
132 message: message.into(),
133 code: Some(code.into()),
134 }
135 }
136}
137
138pub trait CustomErrorFactory {
143 fn create_error(&self) -> WriteError;
145}
146
147pub struct StructureError {
149 format: EcoString,
151 args: Vec<EcoString>,
153}
154
155impl StructureError {
156 pub fn new<S: Into<EcoString>>(format: S) -> Self {
158 Self {
159 format: format.into(),
160 args: Vec::new(),
161 }
162 }
163
164 pub fn arg<S: Into<EcoString>>(mut self, arg: S) -> Self {
166 self.args.push(arg.into());
167 self
168 }
169}
170
171impl CustomErrorFactory for StructureError {
172 fn create_error(&self) -> WriteError {
173 let message = match self.args.len() {
174 0 => self.format.clone(),
175 1 => self.format.replace("{}", &self.args[0]),
176 _ => {
177 let mut result = self.format.to_string();
178 for arg in &self.args {
179 if let Some(pos) = result.find("{}") {
180 result.replace_range(pos..pos + 2, arg);
181 }
182 }
183 EcoString::from(result)
184 }
185 };
186
187 WriteError::InvalidStructure(message)
188 }
189}
190
191pub struct CodedError {
193 message: EcoString,
195 code: EcoString,
197}
198
199impl CodedError {
200 pub fn new<S1: Into<EcoString>, S2: Into<EcoString>>(message: S1, code: S2) -> Self {
202 Self {
203 message: message.into(),
204 code: code.into(),
205 }
206 }
207}
208
209impl CustomErrorFactory for CodedError {
210 fn create_error(&self) -> WriteError {
211 WriteError::custom_with_code(&self.message, &self.code)
212 }
213}
214
215pub trait WriteResultExt<T> {
217 fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError>;
219}
220
221impl<T> WriteResultExt<T> for Result<T, WriteError> {
222 fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError> {
223 Err(factory.create_error())
224 }
225}