use std::error::Error;
use std::fmt::{self, Display};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WriteError {
InvalidHeadingLevel(u8),
NewlineInInlineElement(String),
FmtError(String),
UnsupportedNodeType,
InvalidStructure(String),
InvalidHtmlTag(String),
InvalidHtmlAttribute(String),
Custom {
message: String,
code: Option<String>,
},
}
impl Display for WriteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WriteError::InvalidHeadingLevel(level) => write!(
f,
"Invalid heading level: {}. Level must be between 1 and 6.",
level
),
WriteError::NewlineInInlineElement(context) => write!(
f,
"Newline character found within an inline element ({}) which is not allowed in strict mode or this context.",
context
),
WriteError::FmtError(msg) => write!(f, "Formatting error: {}", msg),
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 names should only contain alphanumeric characters, underscores, colons, or hyphens.", tag)
},
WriteError::InvalidHtmlAttribute(attr) => {
write!(f, "Invalid HTML attribute name: '{}'. Attribute names should only contain alphanumeric characters, underscores, colons, dots, or hyphens.", attr)
},
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())
}
}
pub type WriteResult<T> = Result<T, WriteError>;
impl WriteError {
pub fn custom<S: Into<String>>(message: S) -> Self {
WriteError::Custom {
message: message.into(),
code: None,
}
}
pub fn custom_with_code<S1: Into<String>, S2: Into<String>>(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: String,
args: Vec<String>,
}
impl StructureError {
pub fn new<S: Into<String>>(format: S) -> Self {
Self {
format: format.into(),
args: Vec::new(),
}
}
pub fn arg<S: Into<String>>(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.clone();
for arg in &self.args {
if let Some(pos) = result.find("{}") {
result.replace_range(pos..pos + 2, arg);
}
}
result
}
};
WriteError::InvalidStructure(message)
}
}
pub struct CodedError {
message: String,
code: String,
}
impl CodedError {
pub fn new<S1: Into<String>, S2: Into<String>>(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())
}
}