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}