use crate::validation::ValidationError;
#[derive(thiserror::Error, Debug)]
pub enum MarkdownError {
#[error("Failed to parse Markdown: {0}")]
ParseError(String),
#[error("Failed to convert Markdown to HTML: {0}")]
ConversionError(String),
#[error("Failed to process custom block: {0}")]
CustomBlockError(String),
#[error("Syntax highlighting error: {0}")]
SyntaxHighlightError(String),
#[error("Invalid Markdown options: {0}")]
InvalidOptionsError(String),
#[error("Failed to load syntax set: {0}")]
SyntaxSetError(String),
#[error(
"Input too large: {size} bytes exceeds limit of {limit} bytes"
)]
InputTooLarge {
size: usize,
limit: usize,
},
#[error("HTML rendering error: {0}")]
RenderError(String),
#[error("Output write error: {0}")]
IoError(#[from] std::io::Error),
}
impl From<ValidationError> for MarkdownError {
fn from(err: ValidationError) -> Self {
MarkdownError::InvalidOptionsError(err.to_string())
}
}
impl From<Vec<(String, ValidationError)>> for MarkdownError {
fn from(errors: Vec<(String, ValidationError)>) -> Self {
let msg = errors
.iter()
.map(|(field, err)| format!("{field}: {err}"))
.collect::<Vec<_>>()
.join("; ");
MarkdownError::InvalidOptionsError(msg)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let cases: Vec<(MarkdownError, &str)> = vec![
(
MarkdownError::ParseError("bad input".into()),
"Failed to parse Markdown: bad input",
),
(
MarkdownError::ConversionError("failed".into()),
"Failed to convert Markdown to HTML: failed",
),
(
MarkdownError::InputTooLarge {
size: 2_000_000,
limit: 1_000_000,
},
"Input too large: 2000000 bytes exceeds limit of 1000000 bytes",
),
(
MarkdownError::RenderError("fmt".into()),
"HTML rendering error: fmt",
),
];
for (error, expected) in cases {
assert_eq!(format!("{error}"), expected);
}
}
#[test]
fn test_from_validation_error() {
let err: MarkdownError = ValidationError::Empty.into();
assert_eq!(
err.to_string(),
"Invalid Markdown options: Value cannot be empty"
);
}
#[test]
fn test_from_validation_error_vec_joins_fields() {
let errs = vec![
("name".into(), ValidationError::Empty),
(
"pattern".into(),
ValidationError::InvalidPattern {
pattern: "email".into(),
},
),
];
let err: MarkdownError = errs.into();
let msg = err.to_string();
assert!(
msg.starts_with("Invalid Markdown options: "),
"expected InvalidOptionsError Display prefix, got: {msg}"
);
assert!(msg.contains("name: Value cannot be empty"));
assert!(
msg.contains("pattern: Value doesn't match pattern: email")
);
assert!(msg.contains("; "));
}
}