use ecow::EcoString;
use crate::writer::html::error::HtmlWriteError as CoreHtmlWriteError;
use std::error::Error;
use std::fmt::{self, Display};
use std::io;
#[derive(Debug)]
pub enum WriteError {
InvalidHeadingLevel(u8),
NewlineInInlineElement(EcoString),
FmtError(EcoString),
IoError(io::Error),
UnsupportedNodeType,
InvalidStructure(EcoString),
InvalidHtmlTag(EcoString),
InvalidHtmlAttribute(EcoString),
HtmlRenderingError(CoreHtmlWriteError),
HtmlFallbackError(EcoString),
Custom {
message: EcoString,
code: Option<EcoString>,
},
}
impl Display for WriteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WriteError::InvalidHeadingLevel(level) => write!(
f,
"Invalid heading level: {level}. Level must be between 1 and 6."
),
WriteError::NewlineInInlineElement(context) => write!(
f,
"Newline character found within an inline element ({context}) which is not allowed in strict mode or this context."
),
WriteError::FmtError(msg) => write!(f, "Formatting error: {msg}"),
WriteError::IoError(err) => write!(f, "I/O error: {err}"),
WriteError::UnsupportedNodeType => {
write!(f, "Unsupported node type encountered during writing.")
},
WriteError::InvalidStructure(msg) => {
write!(f, "Invalid structure: {msg}")
},
WriteError::InvalidHtmlTag(tag) => {
write!(f, "Invalid HTML tag name: '{tag}'. Tag names should only contain alphanumeric characters, underscores, colons, or hyphens.")
},
WriteError::InvalidHtmlAttribute(attr) => {
write!(f, "Invalid HTML attribute name: '{attr}'. Attribute names should only contain alphanumeric characters, underscores, colons, dots, or hyphens.")
},
WriteError::HtmlRenderingError(html_err) => {
write!(f, "Error during HTML rendering phase: {html_err}")
},
WriteError::HtmlFallbackError(msg) => {
write!(f, "Error during HTML fallback rendering: {msg}")
},
WriteError::Custom { message, code } => {
if let Some(code) = code {
write!(f, "Custom error [{code}]: {message}")
} else {
write!(f, "Custom error: {message}")
}
}
}
}
}
impl Error for WriteError {}
impl From<fmt::Error> for WriteError {
fn from(err: fmt::Error) -> Self {
WriteError::FmtError(err.to_string().into())
}
}
impl From<io::Error> for WriteError {
fn from(err: io::Error) -> Self {
WriteError::IoError(err)
}
}
impl From<CoreHtmlWriteError> for WriteError {
fn from(err: CoreHtmlWriteError) -> Self {
match err {
CoreHtmlWriteError::InvalidHtmlTag(tag) => WriteError::InvalidHtmlTag(tag.into()),
CoreHtmlWriteError::InvalidHtmlAttribute(attr) => {
WriteError::InvalidHtmlAttribute(attr.into())
}
other_html_err => WriteError::HtmlRenderingError(other_html_err),
}
}
}
pub type WriteResult<T> = Result<T, WriteError>;
impl WriteError {
pub fn custom<S: Into<EcoString>>(message: S) -> Self {
WriteError::Custom {
message: message.into(),
code: None,
}
}
pub fn custom_with_code<S1: Into<EcoString>, S2: Into<EcoString>>(
message: S1,
code: S2,
) -> Self {
WriteError::Custom {
message: message.into(),
code: Some(code.into()),
}
}
}
pub trait CustomErrorFactory {
fn create_error(&self) -> WriteError;
}
pub struct StructureError {
format: EcoString,
args: Vec<EcoString>,
}
impl StructureError {
pub fn new<S: Into<EcoString>>(format: S) -> Self {
Self {
format: format.into(),
args: Vec::new(),
}
}
pub fn arg<S: Into<EcoString>>(mut self, arg: S) -> Self {
self.args.push(arg.into());
self
}
}
impl CustomErrorFactory for StructureError {
fn create_error(&self) -> WriteError {
let message = match self.args.len() {
0 => self.format.clone(),
1 => self.format.replace("{}", &self.args[0]),
_ => {
let mut result = self.format.to_string();
for arg in &self.args {
if let Some(pos) = result.find("{}") {
result.replace_range(pos..pos + 2, arg);
}
}
EcoString::from(result)
}
};
WriteError::InvalidStructure(message)
}
}
pub struct CodedError {
message: EcoString,
code: EcoString,
}
impl CodedError {
pub fn new<S1: Into<EcoString>, S2: Into<EcoString>>(message: S1, code: S2) -> Self {
Self {
message: message.into(),
code: code.into(),
}
}
}
impl CustomErrorFactory for CodedError {
fn create_error(&self) -> WriteError {
WriteError::custom_with_code(&self.message, &self.code)
}
}
pub trait WriteResultExt<T> {
fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError>;
}
impl<T> WriteResultExt<T> for Result<T, WriteError> {
fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError> {
Err(factory.create_error())
}
}