1use alloc::{format, string::String};
15use core::fmt;
16
17#[cfg(not(feature = "std"))]
18extern crate alloc;
19#[cfg(feature = "std")]
20use thiserror::Error;
21
22#[cfg_attr(feature = "std", derive(Error))]
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum CoreError {
29 Parse(crate::parser::ParseError),
31
32 Tokenization(String),
34
35 Analysis(String),
37
38 Plugin(String),
40
41 InvalidColor(String),
43
44 InvalidNumeric(String),
46
47 InvalidTime(String),
49
50 Utf8Error { position: usize, message: String },
52
53 Io(String),
55
56 OutOfMemory(String),
58
59 Config(String),
61
62 Validation(String),
64
65 FeatureNotSupported {
67 feature: String,
68 required_feature: String,
69 },
70
71 VersionIncompatible { message: String },
73
74 ResourceLimitExceeded {
76 resource: String,
77 current: usize,
78 limit: usize,
79 },
80
81 SecurityViolation(String),
83
84 Internal(String),
86}
87
88impl CoreError {
89 pub fn parse<T: fmt::Display>(message: T) -> Self {
91 Self::Parse(crate::parser::ParseError::IoError {
92 message: format!("{message}"),
93 })
94 }
95
96 pub fn internal<T: fmt::Display>(message: T) -> Self {
98 Self::Internal(format!("{message}"))
99 }
100
101 #[must_use]
103 pub const fn is_recoverable(&self) -> bool {
104 match self {
105 Self::Parse(parse_err) => !matches!(
106 parse_err,
107 crate::parser::ParseError::OutOfMemory { .. }
108 | crate::parser::ParseError::InputTooLarge { .. }
109 | crate::parser::ParseError::InternalError { .. }
110 ),
111 Self::Tokenization(_)
112 | Self::InvalidColor(_)
113 | Self::InvalidNumeric(_)
114 | Self::InvalidTime(_)
115 | Self::Validation(_)
116 | Self::Analysis(_)
117 | Self::Plugin(_)
118 | Self::Utf8Error { .. }
119 | Self::Io(_)
120 | Self::Config(_)
121 | Self::FeatureNotSupported { .. }
122 | Self::VersionIncompatible { .. } => true,
123
124 Self::OutOfMemory(_)
125 | Self::ResourceLimitExceeded { .. }
126 | Self::SecurityViolation(_)
127 | Self::Internal(_) => false,
128 }
129 }
130
131 #[must_use]
133 pub const fn is_internal_bug(&self) -> bool {
134 matches!(self, Self::Internal(_))
135 }
136
137 #[must_use]
139 pub const fn as_parse_error(&self) -> Option<&crate::parser::ParseError> {
140 match self {
141 Self::Parse(parse_err) => Some(parse_err),
142 _ => None,
143 }
144 }
145
146 #[must_use]
148 pub const fn line_number(&self) -> Option<usize> {
149 match self {
150 Self::Parse(
151 crate::parser::ParseError::ExpectedSectionHeader { line }
152 | crate::parser::ParseError::UnclosedSectionHeader { line }
153 | crate::parser::ParseError::UnknownSection { line, .. }
154 | crate::parser::ParseError::InvalidFieldFormat { line }
155 | crate::parser::ParseError::InvalidFormatLine { line, .. }
156 | crate::parser::ParseError::FieldCountMismatch { line, .. }
157 | crate::parser::ParseError::InvalidTimeFormat { line, .. }
158 | crate::parser::ParseError::InvalidColorFormat { line, .. }
159 | crate::parser::ParseError::InvalidNumericValue { line, .. }
160 | crate::parser::ParseError::InvalidStyleOverride { line, .. }
161 | crate::parser::ParseError::InvalidDrawingCommand { line, .. }
162 | crate::parser::ParseError::UuDecodeError { line, .. }
163 | crate::parser::ParseError::MaxNestingDepth { line, .. }
164 | crate::parser::ParseError::InternalError { line, .. },
165 ) => Some(*line),
166 Self::Utf8Error { position, .. } => Some(*position),
167 _ => None,
168 }
169 }
170
171 #[must_use]
173 pub fn is_parse_error_type(&self, error_type: &str) -> bool {
174 match self {
175 Self::Parse(parse_err) => matches!(
176 (error_type, parse_err),
177 (
178 "section_header",
179 crate::parser::ParseError::ExpectedSectionHeader { .. }
180 ) | (
181 "unclosed_header",
182 crate::parser::ParseError::UnclosedSectionHeader { .. }
183 ) | (
184 "unknown_section",
185 crate::parser::ParseError::UnknownSection { .. }
186 ) | (
187 "field_format",
188 crate::parser::ParseError::InvalidFieldFormat { .. }
189 | crate::parser::ParseError::FieldCountMismatch { .. }
190 ) | (
191 "time_format",
192 crate::parser::ParseError::InvalidTimeFormat { .. }
193 ) | (
194 "color_format",
195 crate::parser::ParseError::InvalidColorFormat { .. }
196 ) | (
197 "numeric_value",
198 crate::parser::ParseError::InvalidNumericValue { .. }
199 ) | ("utf8", crate::parser::ParseError::Utf8Error { .. })
200 ),
201 _ => false,
202 }
203 }
204}
205
206pub type Result<T> = core::result::Result<T, CoreError>;
208
209#[cfg(not(feature = "std"))]
211impl fmt::Display for CoreError {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 match self {
214 Self::Parse(parse_err) => write!(f, "Parse error: {parse_err}"),
215 Self::Tokenization(msg) => write!(f, "Tokenization error: {msg}"),
216 Self::Analysis(msg) => write!(f, "Analysis error: {msg}"),
217 Self::Plugin(msg) => write!(f, "Plugin error: {msg}"),
218 Self::InvalidColor(msg) => write!(f, "Invalid color format: {msg}"),
219 Self::InvalidNumeric(msg) => write!(f, "Invalid numeric value: {msg}"),
220 Self::InvalidTime(msg) => write!(f, "Invalid time format: {msg}"),
221 Self::Utf8Error { position, message } => {
222 write!(f, "UTF-8 encoding error at position {position}: {message}")
223 }
224 Self::Io(msg) => write!(f, "I/O error: {msg}"),
225 Self::OutOfMemory(msg) => write!(f, "Memory allocation failed: {msg}"),
226 Self::Config(msg) => write!(f, "Configuration error: {msg}"),
227 Self::Validation(msg) => write!(f, "Validation error: {msg}"),
228 Self::FeatureNotSupported {
229 feature,
230 required_feature,
231 } => {
232 write!(
233 f,
234 "Feature not supported: {feature} (requires feature '{required_feature}')"
235 )
236 }
237 Self::VersionIncompatible { message } => {
238 write!(f, "Version incompatibility: {message}")
239 }
240 Self::ResourceLimitExceeded {
241 resource,
242 current,
243 limit,
244 } => {
245 write!(f, "Resource limit exceeded: {resource} ({current}/{limit})")
246 }
247 Self::SecurityViolation(msg) => write!(f, "Security policy violation: {msg}"),
248 Self::Internal(msg) => {
249 write!(f, "Internal error: {msg} (this is a bug, please report)")
250 }
251 }
252 }
253}
254#[cfg(not(feature = "std"))]
256impl core::error::Error for CoreError {}
257#[cfg(feature = "std")]
259impl fmt::Display for CoreError {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 match self {
262 Self::Parse(parse_err) => write!(f, "Parse error: {parse_err}"),
263 Self::Tokenization(msg) => write!(f, "Tokenization error: {msg}"),
264 Self::Analysis(msg) => write!(f, "Analysis error: {msg}"),
265 Self::Plugin(msg) => write!(f, "Plugin error: {msg}"),
266 Self::InvalidColor(msg) => write!(f, "Invalid color format: {msg}"),
267 Self::InvalidNumeric(msg) => write!(f, "Invalid numeric value: {msg}"),
268 Self::InvalidTime(msg) => write!(f, "Invalid time format: {msg}"),
269 Self::Utf8Error { position, message } => {
270 write!(f, "UTF-8 encoding error at position {position}: {message}")
271 }
272 Self::Io(msg) => write!(f, "I/O error: {msg}"),
273 Self::OutOfMemory(msg) => write!(f, "Memory allocation failed: {msg}"),
274 Self::Config(msg) => write!(f, "Configuration error: {msg}"),
275 Self::Validation(msg) => write!(f, "Validation error: {msg}"),
276 Self::FeatureNotSupported {
277 feature,
278 required_feature,
279 } => {
280 write!(
281 f,
282 "Feature not supported: {feature} (requires feature '{required_feature}')"
283 )
284 }
285 Self::VersionIncompatible { message } => {
286 write!(f, "Version incompatibility: {message}")
287 }
288 Self::ResourceLimitExceeded {
289 resource,
290 current,
291 limit,
292 } => {
293 write!(f, "Resource limit exceeded: {resource} ({current}/{limit})")
294 }
295 Self::SecurityViolation(msg) => write!(f, "Security policy violation: {msg}"),
296 Self::Internal(msg) => {
297 write!(f, "Internal error: {msg} (this is a bug, please report)")
298 }
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn error_creation() {
309 let parse_err = CoreError::parse("test message");
310 assert!(matches!(parse_err, CoreError::Parse(_)));
311 }
312
313 #[test]
314 fn internal_error() {
315 let internal_err = CoreError::internal("something went wrong");
316 assert!(matches!(internal_err, CoreError::Internal(_)));
317 assert!(internal_err.is_internal_bug());
318 assert!(!internal_err.is_recoverable());
319 }
320
321 #[test]
322 fn error_recoverability() {
323 assert!(CoreError::parse("test").is_recoverable());
324 assert!(!CoreError::internal("test").is_recoverable());
325 }
326
327 #[test]
328 fn internal_bug_detection() {
329 assert!(CoreError::internal("test").is_internal_bug());
330 assert!(!CoreError::parse("test").is_internal_bug());
331 }
332}