cmark_writer/
error.rs

1//! Error handling for CommonMark writer.
2//!
3//! This module provides error types and implementations for handling errors
4//! that can occur during CommonMark writing.
5
6use std::error::Error;
7use std::fmt::{self, Display};
8
9/// Errors that can occur during CommonMark writing.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum WriteError {
12    /// An invalid heading level was encountered (must be 1-6).
13    InvalidHeadingLevel(u8),
14    /// A newline character was found in an inline element where it's not allowed (e.g., in strict mode or specific contexts like table cells, link text, image alt text).
15    NewlineInInlineElement(String),
16    /// An underlying formatting error occurred.
17    FmtError(String),
18    /// An unsupported node type was encountered.
19    UnsupportedNodeType,
20    /// Invalid structure in a node (e.g., mismatched table columns)
21    InvalidStructure(String),
22    /// A custom error with a message and optional error code.
23    Custom {
24        /// Custom error message
25        message: String,
26        /// Optional error code for programmatic identification
27        code: Option<String>,
28    },
29}
30
31impl Display for WriteError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            WriteError::InvalidHeadingLevel(level) => write!(
35                f,
36                "Invalid heading level: {}. Level must be between 1 and 6.",
37                level
38            ),
39            WriteError::NewlineInInlineElement(context) => write!(
40                f,
41                "Newline character found within an inline element ({}) which is not allowed in strict mode or this context.",
42                context
43            ),
44            WriteError::FmtError(msg) => write!(f, "Formatting error: {}", msg),
45            WriteError::UnsupportedNodeType => {
46                write!(f, "Unsupported node type encountered during writing.")
47            },
48            WriteError::InvalidStructure(msg) => {
49                write!(f, "Invalid structure: {}", msg)
50            },
51            WriteError::Custom { message, code } => {
52                if let Some(code) = code {
53                    write!(f, "Custom error [{}]: {}", code, message)
54                } else {
55                    write!(f, "Custom error: {}", message)
56                }
57            }
58        }
59    }
60}
61
62impl Error for WriteError {}
63
64// Allow converting fmt::Error into WriteError for convenience when using `?`
65impl From<fmt::Error> for WriteError {
66    fn from(err: fmt::Error) -> Self {
67        WriteError::FmtError(err.to_string())
68    }
69}
70
71/// Result type alias for writer operations.
72pub type WriteResult<T> = Result<T, WriteError>;
73
74/// Convenience methods for creating custom errors
75impl WriteError {
76    /// Create a new custom error with a message
77    pub fn custom<S: Into<String>>(message: S) -> Self {
78        WriteError::Custom {
79            message: message.into(),
80            code: None,
81        }
82    }
83
84    /// Create a new custom error with a message and error code
85    pub fn custom_with_code<S1: Into<String>, S2: Into<String>>(message: S1, code: S2) -> Self {
86        WriteError::Custom {
87            message: message.into(),
88            code: Some(code.into()),
89        }
90    }
91}
92
93/// Trait to define custom error factories for WriteError
94///
95/// This trait allows extending WriteError with custom error constructors
96/// while allowing both library and user code to define their own error types.
97pub trait CustomErrorFactory {
98    /// Create an error from this factory
99    fn create_error(&self) -> WriteError;
100}
101
102/// Struct to create structure errors with formatted messages
103pub struct StructureError {
104    /// Format string for the error message
105    format: String,
106    /// Arguments for formatting
107    args: Vec<String>,
108}
109
110impl StructureError {
111    /// Create a new structure error with a format string and arguments
112    pub fn new<S: Into<String>>(format: S) -> Self {
113        Self {
114            format: format.into(),
115            args: Vec::new(),
116        }
117    }
118
119    /// Add an argument to the format string
120    pub fn arg<S: Into<String>>(mut self, arg: S) -> Self {
121        self.args.push(arg.into());
122        self
123    }
124}
125
126impl CustomErrorFactory for StructureError {
127    fn create_error(&self) -> WriteError {
128        let message = match self.args.len() {
129            0 => self.format.clone(),
130            1 => self.format.replace("{}", &self.args[0]),
131            _ => {
132                let mut result = self.format.clone();
133                for arg in &self.args {
134                    if let Some(pos) = result.find("{}") {
135                        result.replace_range(pos..pos + 2, arg);
136                    }
137                }
138                result
139            }
140        };
141
142        WriteError::InvalidStructure(message)
143    }
144}
145
146/// Struct to create custom errors with codes
147pub struct CodedError {
148    /// The error message
149    message: String,
150    /// The error code
151    code: String,
152}
153
154impl CodedError {
155    /// Create a new custom error with message and code
156    pub fn new<S1: Into<String>, S2: Into<String>>(message: S1, code: S2) -> Self {
157        Self {
158            message: message.into(),
159            code: code.into(),
160        }
161    }
162}
163
164impl CustomErrorFactory for CodedError {
165    fn create_error(&self) -> WriteError {
166        WriteError::custom_with_code(&self.message, &self.code)
167    }
168}
169
170/// Extensions for Result<T, WriteError> to work with custom error factories
171pub trait WriteResultExt<T> {
172    /// Convert a custom error factory into an Err result
173    fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError>;
174}
175
176impl<T> WriteResultExt<T> for Result<T, WriteError> {
177    fn custom_error<F: CustomErrorFactory>(factory: F) -> Result<T, WriteError> {
178        Err(factory.create_error())
179    }
180}