typstify_core/
error.rs

1//! Error types for the Typstify core library.
2
3use std::path::PathBuf;
4
5use thiserror::Error;
6
7/// Result type alias using `CoreError`.
8pub type Result<T> = std::result::Result<T, CoreError>;
9
10/// Core error types for Typstify.
11#[derive(Error, Debug)]
12pub enum CoreError {
13    /// Configuration loading or parsing error.
14    #[error("Configuration error: {message}")]
15    Config {
16        message: String,
17        #[source]
18        source: Option<Box<dyn std::error::Error + Send + Sync>>,
19    },
20
21    /// Content parsing error with file location.
22    #[error("Parse error in {path}: {message}")]
23    Parse { path: PathBuf, message: String },
24
25    /// Frontmatter parsing error.
26    #[error("Frontmatter error in {path}: {message}")]
27    Frontmatter { path: PathBuf, message: String },
28
29    /// Template rendering error.
30    #[error("Template error: {0}")]
31    Template(String),
32
33    /// Search index error.
34    #[error("Search error: {0}")]
35    Search(String),
36
37    /// File system I/O error.
38    #[error("IO error: {0}")]
39    Io(#[from] std::io::Error),
40
41    /// TOML parsing error.
42    #[error("TOML parse error: {0}")]
43    Toml(#[from] toml::de::Error),
44
45    /// YAML parsing error.
46    #[error("YAML parse error: {0}")]
47    Yaml(#[from] serde_yaml::Error),
48
49    /// Generic configuration crate error.
50    #[error("Config crate error: {0}")]
51    ConfigCrate(#[from] config::ConfigError),
52}
53
54impl CoreError {
55    /// Create a new configuration error with a message.
56    pub fn config(message: impl Into<String>) -> Self {
57        Self::Config {
58            message: message.into(),
59            source: None,
60        }
61    }
62
63    /// Create a new configuration error with source.
64    pub fn config_with_source(
65        message: impl Into<String>,
66        source: impl std::error::Error + Send + Sync + 'static,
67    ) -> Self {
68        Self::Config {
69            message: message.into(),
70            source: Some(Box::new(source)),
71        }
72    }
73
74    /// Create a new parse error.
75    pub fn parse(path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
76        Self::Parse {
77            path: path.into(),
78            message: message.into(),
79        }
80    }
81
82    /// Create a new frontmatter error.
83    pub fn frontmatter(path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
84        Self::Frontmatter {
85            path: path.into(),
86            message: message.into(),
87        }
88    }
89
90    /// Create a new template error.
91    pub fn template(message: impl Into<String>) -> Self {
92        Self::Template(message.into())
93    }
94
95    /// Create a new search error.
96    pub fn search(message: impl Into<String>) -> Self {
97        Self::Search(message.into())
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_config_error() {
107        let err = CoreError::config("missing field");
108        assert!(err.to_string().contains("Configuration error"));
109        assert!(err.to_string().contains("missing field"));
110    }
111
112    #[test]
113    fn test_parse_error() {
114        let err = CoreError::parse("content/post.md", "invalid syntax");
115        assert!(err.to_string().contains("Parse error"));
116        assert!(err.to_string().contains("content/post.md"));
117    }
118
119    #[test]
120    fn test_frontmatter_error() {
121        let err = CoreError::frontmatter("content/post.md", "missing title");
122        assert!(err.to_string().contains("Frontmatter error"));
123        assert!(err.to_string().contains("missing title"));
124    }
125
126    #[test]
127    fn test_io_error_conversion() {
128        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
129        let err: CoreError = io_err.into();
130        assert!(err.to_string().contains("IO error"));
131    }
132}