1use crate::validation::ValidationError;
4
5#[derive(thiserror::Error, Debug)]
7pub enum MarkdownError {
8 #[error("Failed to parse Markdown: {0}")]
10 ParseError(String),
11
12 #[error("Failed to convert Markdown to HTML: {0}")]
14 ConversionError(String),
15
16 #[error("Failed to process custom block: {0}")]
18 CustomBlockError(String),
19
20 #[error("Syntax highlighting error: {0}")]
22 SyntaxHighlightError(String),
23
24 #[error("Invalid Markdown options: {0}")]
26 InvalidOptionsError(String),
27
28 #[error("Failed to load syntax set: {0}")]
30 SyntaxSetError(String),
31
32 #[error(
34 "Input too large: {size} bytes exceeds limit of {limit} bytes"
35 )]
36 InputTooLarge {
37 size: usize,
39 limit: usize,
41 },
42
43 #[error("HTML rendering error: {0}")]
45 RenderError(String),
46
47 #[error("Output write error: {0}")]
49 IoError(#[from] std::io::Error),
50}
51
52impl From<ValidationError> for MarkdownError {
55 fn from(err: ValidationError) -> Self {
56 MarkdownError::InvalidOptionsError(err.to_string())
57 }
58}
59
60impl From<Vec<(String, ValidationError)>> for MarkdownError {
70 fn from(errors: Vec<(String, ValidationError)>) -> Self {
71 let msg = errors
72 .iter()
73 .map(|(field, err)| format!("{field}: {err}"))
74 .collect::<Vec<_>>()
75 .join("; ");
76 MarkdownError::InvalidOptionsError(msg)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_error_display() {
86 let cases: Vec<(MarkdownError, &str)> = vec![
87 (
88 MarkdownError::ParseError("bad input".into()),
89 "Failed to parse Markdown: bad input",
90 ),
91 (
92 MarkdownError::ConversionError("failed".into()),
93 "Failed to convert Markdown to HTML: failed",
94 ),
95 (
96 MarkdownError::InputTooLarge {
97 size: 2_000_000,
98 limit: 1_000_000,
99 },
100 "Input too large: 2000000 bytes exceeds limit of 1000000 bytes",
101 ),
102 (
103 MarkdownError::RenderError("fmt".into()),
104 "HTML rendering error: fmt",
105 ),
106 ];
107
108 for (error, expected) in cases {
109 assert_eq!(format!("{error}"), expected);
110 }
111 }
112
113 #[test]
114 fn test_from_validation_error() {
115 let err: MarkdownError = ValidationError::Empty.into();
121 assert_eq!(
122 err.to_string(),
123 "Invalid Markdown options: Value cannot be empty"
124 );
125 }
126
127 #[test]
128 fn test_from_validation_error_vec_joins_fields() {
129 let errs = vec![
130 ("name".into(), ValidationError::Empty),
131 (
132 "pattern".into(),
133 ValidationError::InvalidPattern {
134 pattern: "email".into(),
135 },
136 ),
137 ];
138 let err: MarkdownError = errs.into();
139 let msg = err.to_string();
140 assert!(
141 msg.starts_with("Invalid Markdown options: "),
142 "expected InvalidOptionsError Display prefix, got: {msg}"
143 );
144 assert!(msg.contains("name: Value cannot be empty"));
145 assert!(
146 msg.contains("pattern: Value doesn't match pattern: email")
147 );
148 assert!(msg.contains("; "));
149 }
150}