frontmatter_gen/error.rs
1//! Error handling for the frontmatter-gen crate.
2//!
3//! This module provides a comprehensive set of error types to handle various
4//! failure scenarios that may occur during front matter parsing, conversion,
5//! and extraction operations. Each error variant includes detailed error
6//! messages and context to aid in debugging and error handling.
7//!
8//! # Error Handling Strategies
9//!
10//! The error system provides several ways to handle errors:
11//!
12//! - **Context-aware errors**: Use `Context` to add line/column information
13//! - **Categorized errors**: Group errors by type using `Category`
14//! - **Error conversion**: Convert from standard errors using `From` implementations
15//! - **Rich error messages**: Detailed error descriptions with context
16//!
17//! # Features
18//!
19//! - Type-safe error handling with descriptive messages
20//! - Support for YAML, TOML, and JSON parsing errors
21//! - Content validation errors with size and depth checks
22//! - Format-specific error handling
23//! - Extraction and conversion error handling
24//!
25//! # Examples
26//!
27//! ```rust
28//! use frontmatter_gen::error::Error;
29//!
30//! fn example() -> Result<(), Error> {
31//! // Example of handling YAML parsing errors
32//! let invalid_yaml = "invalid: : yaml";
33//! match serde_yml::from_str::<serde_yml::Value>(invalid_yaml) {
34//! Ok(_) => Ok(()),
35//! Err(e) => Err(Error::YamlParseError { source: e.into() }),
36//! }
37//! }
38//! ```
39
40use serde_json::Error as JsonError;
41use serde_yml::Error as YamlError;
42use std::sync::Arc;
43use thiserror::Error;
44
45/// Provides additional context for front matter errors.
46#[derive(Debug, Clone)]
47pub struct Context {
48 /// Line number where the error occurred.
49 pub line: Option<usize>,
50 /// Column number where the error occurred.
51 pub column: Option<usize>,
52 /// Snippet of the content where the error occurred.
53 pub snippet: Option<String>,
54}
55
56impl std::fmt::Display for Context {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(
59 f,
60 "at {}:{}",
61 self.line.unwrap_or(0),
62 self.column.unwrap_or(0)
63 )?;
64 if let Some(snippet) = &self.snippet {
65 write!(f, " near '{}'", snippet)?;
66 }
67 Ok(())
68 }
69}
70
71/// Represents errors that can occur during front matter operations.
72///
73/// This enumeration uses the `thiserror` crate to provide structured error
74/// messages, improving the ease of debugging and handling errors encountered
75/// in front matter processing.
76///
77/// Each variant represents a specific type of error that may occur during
78/// front matter operations, with appropriate context and error details.
79#[derive(Error, Debug)]
80#[non_exhaustive]
81pub enum Error {
82 /// Content exceeds the maximum allowed size.
83 ///
84 /// This error occurs when the content size is larger than the configured
85 /// maximum limit.
86 ///
87 /// # Fields
88 ///
89 /// * `size` - The actual size of the content
90 /// * `max` - The maximum allowed size
91 #[error("Your front matter contains too many fields ({size}). The maximum allowed is {max}.")]
92 ContentTooLarge {
93 /// The actual size of the content
94 size: usize,
95 /// The maximum allowed size
96 max: usize,
97 },
98
99 /// Nesting depth exceeds the maximum allowed.
100 ///
101 /// This error occurs when the structure's nesting depth is greater than
102 /// the configured maximum depth.
103 #[error(
104 "Your front matter is nested too deeply ({depth} levels). The maximum allowed nesting depth is {max}."
105 )]
106 NestingTooDeep {
107 /// The actual nesting depth
108 depth: usize,
109 /// The maximum allowed depth
110 max: usize,
111 },
112
113 /// Error occurred whilst parsing YAML content.
114 ///
115 /// This error occurs when the YAML parser encounters invalid syntax or
116 /// structure.
117 #[error("Failed to parse YAML: {source}")]
118 YamlParseError {
119 /// The original error from the YAML parser
120 source: Arc<YamlError>,
121 },
122
123 /// Error occurred whilst parsing TOML content.
124 ///
125 /// This error occurs when the TOML parser encounters invalid syntax or
126 /// structure.
127 #[error("Failed to parse TOML: {0}")]
128 TomlParseError(#[from] toml::de::Error),
129
130 /// Error occurred whilst parsing JSON content.
131 ///
132 /// This error occurs when the JSON parser encounters invalid syntax or
133 /// structure.
134 #[error("Failed to parse JSON: {0}")]
135 JsonParseError(Arc<JsonError>),
136
137 /// The front matter format is invalid or unsupported.
138 ///
139 /// This error occurs when the front matter format cannot be determined or
140 /// is not supported by the library.
141 #[error("Invalid front matter format")]
142 InvalidFormat,
143
144 /// Error occurred during conversion between formats.
145 ///
146 /// This error occurs when converting front matter from one format to another
147 /// fails.
148 #[error("Failed to convert front matter: {0}")]
149 ConversionError(String),
150
151 /// Generic error during parsing.
152 ///
153 /// This error occurs when a parsing operation fails with a generic error.
154 #[error("Failed to parse front matter: {0}")]
155 ParseError(String),
156
157 /// Unsupported or unknown front matter format was detected.
158 ///
159 /// This error occurs when an unsupported front matter format is encountered
160 /// at a specific line.
161 #[error("Unsupported front matter format detected at line {line}")]
162 UnsupportedFormat {
163 /// The line number where the unsupported format was encountered
164 line: usize,
165 },
166
167 /// No front matter content was found.
168 ///
169 /// This error occurs when attempting to extract front matter from content
170 /// that does not contain any front matter section.
171 #[error("No front matter found in the content")]
172 NoFrontmatterFound,
173
174 /// Invalid JSON front matter.
175 ///
176 /// This error occurs when the JSON front matter is malformed or invalid.
177 #[error(
178 "Invalid JSON front matter: malformed or invalid structure."
179 )]
180 InvalidJson,
181
182 /// Invalid URL format.
183 ///
184 /// This error occurs when an invalid URL is encountered in the front matter.
185 #[error(
186 "Invalid URL: {0}. Ensure the URL is well-formed and valid."
187 )]
188 InvalidUrl(String),
189
190 /// Invalid TOML front matter.
191 ///
192 /// This error occurs when the TOML front matter is malformed or invalid.
193 #[error(
194 "Invalid TOML front matter: malformed or invalid structure."
195 )]
196 InvalidToml,
197
198 /// Invalid YAML front matter.
199 ///
200 /// This error occurs when the YAML front matter is malformed or invalid.
201 #[error(
202 "Invalid YAML front matter: malformed or invalid structure."
203 )]
204 InvalidYaml,
205
206 /// Invalid language code.
207 ///
208 /// This error occurs when an invalid language code is encountered in the
209 /// front matter.
210 #[error("Invalid language code: {0}")]
211 InvalidLanguage(String),
212
213 /// JSON front matter exceeds maximum nesting depth.
214 ///
215 /// This error occurs when the JSON front matter structure exceeds the
216 /// maximum allowed nesting depth.
217 #[error("JSON front matter exceeds maximum nesting depth")]
218 JsonDepthLimitExceeded,
219
220 /// Error during front matter extraction.
221 ///
222 /// This error occurs when there is a problem extracting front matter from
223 /// the content.
224 #[error("Extraction error: {0}")]
225 ExtractionError(String),
226
227 /// Serialization or deserialization error.
228 ///
229 /// This error occurs when there is a problem serializing or deserializing
230 /// content.
231 #[error("Serialization or deserialization error: {source}")]
232 SerdeError {
233 /// The original error from the serde library
234 source: Arc<serde_json::Error>,
235 },
236
237 /// Input validation error.
238 ///
239 /// This error occurs when the input fails validation checks.
240 #[error("Input validation error: {0}")]
241 ValidationError(String),
242
243 /// Generic error with a custom message.
244 ///
245 /// This error occurs when a generic error is encountered with a custom message.
246 #[error("Generic error: {0}")]
247 Other(String),
248}
249
250impl Clone for Error {
251 fn clone(&self) -> Self {
252 match self {
253 Self::ContentTooLarge { size, max } => {
254 Self::ContentTooLarge {
255 size: *size,
256 max: *max,
257 }
258 }
259 Self::NestingTooDeep { depth, max } => {
260 Self::NestingTooDeep {
261 depth: *depth,
262 max: *max,
263 }
264 }
265 Self::YamlParseError { source } => Self::YamlParseError {
266 source: Arc::clone(source),
267 },
268 Self::JsonParseError(err) => {
269 Self::JsonParseError(Arc::<serde_json::Error>::clone(
270 err,
271 ))
272 }
273 Self::TomlParseError(err) => {
274 Self::TomlParseError(err.clone())
275 }
276 Self::SerdeError { source } => Self::SerdeError {
277 source: Arc::clone(source),
278 },
279 Self::ConversionError(msg) => {
280 Self::ConversionError(msg.clone())
281 }
282 Self::ParseError(msg) => Self::ParseError(msg.clone()),
283 Self::UnsupportedFormat { line } => {
284 Self::UnsupportedFormat { line: *line }
285 }
286 Self::NoFrontmatterFound => Self::NoFrontmatterFound,
287 Self::InvalidJson => Self::InvalidJson,
288 Self::InvalidToml => Self::InvalidToml,
289 Self::InvalidYaml => Self::InvalidYaml,
290 Self::JsonDepthLimitExceeded => {
291 Self::JsonDepthLimitExceeded
292 }
293 Self::ExtractionError(msg) => {
294 Self::ExtractionError(msg.clone())
295 }
296 Self::ValidationError(msg) => {
297 Self::ValidationError(msg.clone())
298 }
299 Self::InvalidUrl(msg) => Self::InvalidUrl(msg.clone()),
300 Self::InvalidLanguage(msg) => {
301 Self::InvalidLanguage(msg.clone())
302 }
303 Self::Other(msg) => Self::Other(msg.clone()),
304 Self::InvalidFormat => Self::InvalidFormat,
305 }
306 }
307}
308
309/// Categories of front matter errors.
310///
311/// This enumeration defines the main categories of errors that can occur
312/// during front matter operations.
313#[derive(Debug, Copy, Clone, Eq, PartialEq)]
314pub enum Category {
315 /// Parsing-related errors.
316 Parsing,
317 /// Validation-related errors.
318 Validation,
319 /// Conversion-related errors.
320 Conversion,
321 /// Configuration-related errors.
322 Configuration,
323}
324
325impl Error {
326 /// Returns the category of the error.
327 ///
328 /// # Returns
329 ///
330 /// Returns the `Category` that best describes this error.
331 #[must_use]
332 pub const fn category(&self) -> Category {
333 match self {
334 Self::YamlParseError { .. }
335 | Self::TomlParseError(_)
336 | Self::JsonParseError(_)
337 | Self::SerdeError { .. }
338 | Self::ParseError(_)
339 | Self::InvalidFormat
340 | Self::UnsupportedFormat { .. }
341 | Self::NoFrontmatterFound
342 | Self::InvalidJson
343 | Self::InvalidToml
344 | Self::InvalidYaml
345 | Self::JsonDepthLimitExceeded
346 | Self::ExtractionError(_)
347 | Self::InvalidUrl(_)
348 | Self::InvalidLanguage(_) => Category::Parsing,
349 Self::ValidationError(_) => Category::Validation,
350 Self::ConversionError(_) => Category::Conversion,
351 Self::ContentTooLarge { .. }
352 | Self::NestingTooDeep { .. }
353 | Self::Other(_) => Category::Configuration,
354 }
355 }
356
357 /// Creates a generic parse error with a custom message.
358 ///
359 /// # Arguments
360 ///
361 /// * `message` - A string slice containing the error message.
362 ///
363 /// # Examples
364 ///
365 /// ```rust
366 /// use frontmatter_gen::error::Error;
367 ///
368 /// let error = Error::generic_parse_error("Invalid syntax");
369 /// assert!(matches!(error, Error::ParseError(_)));
370 /// ```
371 #[must_use]
372 pub fn generic_parse_error(message: &str) -> Self {
373 Self::ParseError(message.to_string())
374 }
375
376 /// Creates an unsupported format error for a specific line.
377 ///
378 /// # Arguments
379 ///
380 /// * `line` - The line number where the unsupported format was detected.
381 ///
382 /// # Examples
383 ///
384 /// ```rust
385 /// use frontmatter_gen::error::Error;
386 ///
387 /// let error = Error::unsupported_format(42);
388 /// assert!(matches!(error, Error::UnsupportedFormat { line: 42 }));
389 /// ```
390 #[must_use]
391 pub const fn unsupported_format(line: usize) -> Self {
392 Self::UnsupportedFormat { line }
393 }
394
395 /// Creates a validation error with a custom message.
396 ///
397 /// # Arguments
398 ///
399 /// * `message` - A string slice containing the validation error message.
400 ///
401 /// # Examples
402 ///
403 /// ```rust
404 /// use frontmatter_gen::error::Error;
405 ///
406 /// let error = Error::validation_error("Invalid character in title");
407 /// assert!(matches!(error, Error::ValidationError(_)));
408 /// ```
409 #[must_use]
410 pub fn validation_error(message: &str) -> Self {
411 Self::ValidationError(message.to_string())
412 }
413
414 /// Adds context to an error.
415 ///
416 /// # Arguments
417 ///
418 /// * `context` - Additional context information about the error.
419 ///
420 /// # Examples
421 ///
422 /// ```rust
423 /// use frontmatter_gen::error::{Error, Context};
424 ///
425 /// let context = Context {
426 /// line: Some(42),
427 /// column: Some(10),
428 /// snippet: Some("invalid content".to_string()),
429 /// };
430 ///
431 /// let error = Error::ParseError("Invalid syntax".to_string())
432 /// .with_context(&context);
433 /// ```
434 #[must_use]
435 pub fn with_context(self, context: &Context) -> Self {
436 let context_info = format!(
437 " (line: {}, column: {})",
438 context.line.unwrap_or(0),
439 context.column.unwrap_or(0)
440 );
441 let snippet_info = context
442 .snippet
443 .as_ref()
444 .map(|s| format!(" near '{}'", s))
445 .unwrap_or_default();
446
447 match self {
448 Self::ParseError(msg) => Self::ParseError(format!(
449 "{msg}{context_info}{snippet_info}"
450 )),
451 Self::YamlParseError { source } => {
452 Self::YamlParseError { source }
453 }
454 _ => self, // For unsupported variants
455 }
456 }
457}
458
459/// Errors that can occur during site generation.
460///
461/// This enum is used to represent higher-level errors encountered during site
462/// generation processes, such as template rendering, file system operations,
463/// and metadata processing.
464#[derive(Error, Debug)]
465pub enum EngineError {
466 /// Error occurred during content processing.
467 #[error("Content processing error: {0}")]
468 ContentError(String),
469
470 /// Error occurred during template processing.
471 #[error("Template processing error: {0}")]
472 TemplateError(String),
473
474 /// Error occurred during asset processing.
475 #[error("Asset processing error: {0}")]
476 AssetError(String),
477
478 /// Error occurred during file system operations.
479 #[error("File system error: {source}")]
480 FileSystemError {
481 /// The original IO error that caused this error.
482 source: std::io::Error,
483 /// Additional context information about the error.
484 context: String,
485 },
486
487 /// Error occurred during metadata processing.
488 #[error("Metadata error: {0}")]
489 MetadataError(String),
490}
491
492impl Clone for EngineError {
493 fn clone(&self) -> Self {
494 match self {
495 Self::ContentError(msg) => Self::ContentError(msg.clone()),
496 Self::TemplateError(msg) => {
497 Self::TemplateError(msg.clone())
498 }
499 Self::AssetError(msg) => Self::AssetError(msg.clone()),
500 Self::FileSystemError { source, context } => {
501 Self::FileSystemError {
502 source: std::io::Error::new(
503 source.kind(),
504 source.to_string(),
505 ),
506 context: context.clone(),
507 }
508 }
509 Self::MetadataError(msg) => {
510 Self::MetadataError(msg.clone())
511 }
512 }
513 }
514}
515
516/// Converts an `EngineError` into an `Error`.
517///
518/// This allows engine errors to be converted into front matter errors when needed,
519/// preserving the error context and message.
520///
521/// # Examples
522///
523/// ```rust
524/// use frontmatter_gen::error::{EngineError, Error};
525/// use std::io;
526///
527/// let engine_error = EngineError::ContentError("content processing failed".to_string());
528/// let frontmatter_error: Error = engine_error.into();
529/// assert!(matches!(frontmatter_error, Error::ParseError(_)));
530/// ```
531impl From<EngineError> for Error {
532 fn from(err: EngineError) -> Self {
533 match err {
534 EngineError::ContentError(msg) => {
535 Self::ParseError(format!("Content error: {}", msg))
536 }
537 EngineError::TemplateError(msg) => {
538 Self::ParseError(format!("Template error: {}", msg))
539 }
540 EngineError::AssetError(msg) => {
541 Self::ParseError(format!("Asset error: {}", msg))
542 }
543 EngineError::FileSystemError { source, context } => {
544 Self::ParseError(format!(
545 "File system error: {} ({})",
546 source, context
547 ))
548 }
549 EngineError::MetadataError(msg) => {
550 Self::ParseError(format!("Metadata error: {}", msg))
551 }
552 }
553 }
554}
555
556/// Converts an IO error (`std::io::Error`) into a front matter `Error`.
557impl From<std::io::Error> for Error {
558 fn from(err: std::io::Error) -> Self {
559 Self::ParseError(err.to_string())
560 }
561}
562
563/// Converts a front matter `Error` into a string.
564impl From<Error> for String {
565 fn from(err: Error) -> Self {
566 err.to_string()
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 /// Tests for the `Error` enum and its associated methods.
573 mod error_tests {
574 use super::super::*;
575
576 /// Test the `ContentTooLarge` error variant.
577 #[test]
578 fn test_content_too_large_error() {
579 let error = Error::ContentTooLarge {
580 size: 1000,
581 max: 500,
582 };
583 assert_eq!(
584 error.to_string(),
585 "Your front matter contains too many fields (1000). The maximum allowed is 500."
586 );
587 }
588
589 /// Test the `NestingTooDeep` error variant.
590 #[test]
591 fn test_nesting_too_deep_error() {
592 let error = Error::NestingTooDeep { depth: 10, max: 5 };
593 assert_eq!(
594 error.to_string(),
595 "Your front matter is nested too deeply (10 levels). The maximum allowed nesting depth is 5."
596 );
597 }
598
599 /// Test the `JsonParseError` error variant.
600 #[test]
601 fn test_json_parse_error() {
602 let json_data = r#"{"key": invalid}"#;
603 let result: Result<serde_json::Value, _> =
604 serde_json::from_str(json_data);
605 assert!(result.is_err());
606 let error =
607 Error::JsonParseError(Arc::new(result.unwrap_err()));
608 assert!(matches!(error, Error::JsonParseError(_)));
609 }
610
611 /// Test the `InvalidFormat` error variant.
612 #[test]
613 fn test_invalid_format_error() {
614 let error = Error::InvalidFormat;
615 assert_eq!(
616 error.to_string(),
617 "Invalid front matter format"
618 );
619 }
620
621 /// Test the `ConversionError` error variant.
622 #[test]
623 fn test_conversion_error() {
624 let error =
625 Error::ConversionError("Conversion failed".to_string());
626 assert_eq!(
627 error.to_string(),
628 "Failed to convert front matter: Conversion failed"
629 );
630 }
631
632 /// Test the `UnsupportedFormat` error variant.
633 #[test]
634 fn test_unsupported_format() {
635 let error = Error::unsupported_format(42);
636 assert!(matches!(
637 error,
638 Error::UnsupportedFormat { line: 42 }
639 ));
640 assert_eq!(
641 error.to_string(),
642 "Unsupported front matter format detected at line 42"
643 );
644 }
645
646 /// Test the `NoFrontmatterFound` error variant.
647 #[test]
648 fn test_no_frontmatter_found() {
649 let error = Error::NoFrontmatterFound;
650 assert_eq!(
651 error.to_string(),
652 "No front matter found in the content"
653 );
654 }
655
656 /// Test the `InvalidJson` error variant.
657 #[test]
658 fn test_invalid_json_error() {
659 let error = Error::InvalidJson;
660 assert_eq!(error.to_string(), "Invalid JSON front matter: malformed or invalid structure.");
661 }
662
663 /// Test the `InvalidUrl` error variant.
664 #[test]
665 fn test_invalid_url_error() {
666 let error =
667 Error::InvalidUrl("http:// invalid.url".to_string());
668 assert_eq!(
669 error.to_string(),
670 "Invalid URL: http:// invalid.url. Ensure the URL is well-formed and valid."
671 );
672 }
673
674 /// Test the `InvalidYaml` error variant.
675 #[test]
676 fn test_invalid_yaml_error() {
677 let error = Error::InvalidYaml;
678 assert_eq!(
679 error.to_string(),
680 "Invalid YAML front matter: malformed or invalid structure."
681 );
682 }
683
684 /// Test the `ValidationError` error variant.
685 #[test]
686 fn test_validation_error() {
687 let error =
688 Error::ValidationError("Invalid title".to_string());
689 assert_eq!(
690 error.to_string(),
691 "Input validation error: Invalid title"
692 );
693 }
694
695 /// Test the `JsonDepthLimitExceeded` error variant.
696 #[test]
697 fn test_json_depth_limit_exceeded() {
698 let error = Error::JsonDepthLimitExceeded;
699 assert_eq!(
700 error.to_string(),
701 "JSON front matter exceeds maximum nesting depth"
702 );
703 }
704
705 /// Test the `category` method for different error variants.
706 #[test]
707 fn test_category_method() {
708 let validation_error =
709 Error::ValidationError("Invalid field".to_string());
710 assert_eq!(
711 validation_error.category(),
712 Category::Validation
713 );
714
715 let conversion_error =
716 Error::ConversionError("Conversion failed".to_string());
717 assert_eq!(
718 conversion_error.category(),
719 Category::Conversion
720 );
721
722 let config_error =
723 Error::ContentTooLarge { size: 100, max: 50 };
724 assert_eq!(
725 config_error.category(),
726 Category::Configuration
727 );
728 }
729
730 /// Test the `Clone` implementation for `Error`.
731 #[test]
732 fn test_error_clone() {
733 let original = Error::ContentTooLarge {
734 size: 200,
735 max: 100,
736 };
737 let cloned = original.clone();
738 assert!(
739 matches!(cloned, Error::ContentTooLarge { size, max } if size == 200 && max == 100)
740 );
741 }
742 }
743
744 /// Tests for the `EngineError` enum.
745 mod engine_error_tests {
746 use super::super::*;
747 use std::io;
748
749 /// Test the `ContentError` variant.
750 #[test]
751 fn test_content_error() {
752 let error = EngineError::ContentError(
753 "Processing failed".to_string(),
754 );
755 assert_eq!(
756 error.to_string(),
757 "Content processing error: Processing failed"
758 );
759 }
760
761 /// Test `EngineError::FileSystemError` conversion to `Error`.
762 #[test]
763 fn test_engine_error_to_error_conversion() {
764 let io_error =
765 io::Error::new(io::ErrorKind::Other, "disk full");
766 let engine_error = EngineError::FileSystemError {
767 source: io_error,
768 context: "Saving file".to_string(),
769 };
770 let converted: Error = engine_error.into();
771 assert!(converted.to_string().contains("disk full"));
772 assert!(converted.to_string().contains("Saving file"));
773 }
774 }
775
776 /// Tests for the `Context` struct.
777 mod context_tests {
778 use super::super::*;
779
780 /// Test the `Display` implementation of `Context`.
781 #[test]
782 fn test_context_display() {
783 let context = Context {
784 line: Some(42),
785 column: Some(10),
786 snippet: Some("invalid key".to_string()),
787 };
788 assert_eq!(
789 context.to_string(),
790 "at 42:10 near 'invalid key'"
791 );
792 }
793
794 /// Test missing fields in `Context`.
795 #[test]
796 fn test_context_missing_fields() {
797 let context = Context {
798 line: None,
799 column: None,
800 snippet: Some("example snippet".to_string()),
801 };
802 assert_eq!(
803 context.to_string(),
804 "at 0:0 near 'example snippet'"
805 );
806 }
807 }
808
809 /// Tests for conversions.
810 mod conversion_tests {
811 use super::super::*;
812 use std::io;
813
814 /// Test the conversion from `std::io::Error` to `Error`.
815 #[test]
816 fn test_io_error_conversion() {
817 let io_error =
818 io::Error::new(io::ErrorKind::NotFound, "file missing");
819 let error: Error = io_error.into();
820 assert!(matches!(error, Error::ParseError(_)));
821 assert!(error.to_string().contains("file missing"));
822 }
823 }
824
825 /// Test the conversion of `EngineError` to `Error`.
826 #[test]
827 fn test_engine_error_conversion() {
828 let engine_error = crate::error::EngineError::ContentError(
829 "content failed".to_string(),
830 );
831 let error: crate::Error = engine_error.into();
832 assert!(matches!(error, crate::Error::ParseError(_)));
833 assert!(error.to_string().contains("content failed"));
834 }
835}