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