tailwind_rs_core/
error.rs

1//! Error types for tailwind-rs-core
2
3use thiserror::Error;
4
5/// Result type alias for tailwind-rs operations
6pub type Result<T> = std::result::Result<T, TailwindError>;
7
8/// Main error type for tailwind-rs operations
9#[derive(Error, Debug)]
10pub enum TailwindError {
11    /// Configuration errors
12    #[error("Configuration error: {message}")]
13    Config { message: String },
14
15    /// Theme-related errors
16    #[error("Theme error: {message}")]
17    Theme { message: String },
18
19    /// Class generation errors
20    #[error("Class generation error: {message}")]
21    ClassGeneration { message: String },
22
23    /// Build process errors
24    #[error("Build error: {message}")]
25    Build { message: String },
26
27    /// Validation errors
28    #[error("Validation error: {message}")]
29    Validation { message: String },
30
31    /// File I/O errors
32    #[error("File I/O error: {0}")]
33    Io(#[from] std::io::Error),
34
35    /// JSON serialization/deserialization errors
36    #[error("JSON error: {0}")]
37    Json(#[from] serde_json::Error),
38
39    /// Generic error wrapper
40    #[error("Generic error: {0}")]
41    Generic(#[from] anyhow::Error),
42
43    /// Regex compilation errors
44    #[error("Regex error: {0}")]
45    Regex(#[from] regex::Error),
46}
47
48impl TailwindError {
49    /// Create a new configuration error
50    pub fn config(message: impl Into<String>) -> Self {
51        Self::Config {
52            message: message.into(),
53        }
54    }
55
56    /// Create a new theme error
57    pub fn theme(message: impl Into<String>) -> Self {
58        Self::Theme {
59            message: message.into(),
60        }
61    }
62
63    /// Create a new class generation error
64    pub fn class_generation(message: impl Into<String>) -> Self {
65        Self::ClassGeneration {
66            message: message.into(),
67        }
68    }
69
70    /// Create a new build error
71    pub fn build(message: impl Into<String>) -> Self {
72        Self::Build {
73            message: message.into(),
74        }
75    }
76
77    /// Create a new validation error
78    pub fn validation(message: impl Into<String>) -> Self {
79        Self::Validation {
80            message: message.into(),
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_config_error() {
91        let error = TailwindError::config("Invalid configuration");
92        assert!(matches!(error, TailwindError::Config { .. }));
93        assert!(error.to_string().contains("Invalid configuration"));
94    }
95
96    #[test]
97    fn test_theme_error() {
98        let error = TailwindError::theme("Theme not found");
99        assert!(matches!(error, TailwindError::Theme { .. }));
100        assert!(error.to_string().contains("Theme not found"));
101    }
102
103    #[test]
104    fn test_class_generation_error() {
105        let error = TailwindError::class_generation("Invalid class name");
106        assert!(matches!(error, TailwindError::ClassGeneration { .. }));
107        assert!(error.to_string().contains("Invalid class name"));
108    }
109
110    #[test]
111    fn test_build_error() {
112        let error = TailwindError::build("Build failed");
113        assert!(matches!(error, TailwindError::Build { .. }));
114        assert!(error.to_string().contains("Build failed"));
115    }
116
117    #[test]
118    fn test_io_error_conversion() {
119        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
120        let tailwind_error: TailwindError = io_error.into();
121        assert!(matches!(tailwind_error, TailwindError::Io(_)));
122    }
123
124    #[test]
125    fn test_json_error_conversion() {
126        let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
127        let tailwind_error: TailwindError = json_error.into();
128        assert!(matches!(tailwind_error, TailwindError::Json(_)));
129    }
130
131    #[test]
132    fn test_generic_error_conversion() {
133        let anyhow_error = anyhow::anyhow!("Generic error");
134        let tailwind_error: TailwindError = anyhow_error.into();
135        assert!(matches!(tailwind_error, TailwindError::Generic(_)));
136    }
137
138    /// Test that all Week 17 error handling features are implemented
139    #[test]
140    fn test_week17_error_handling() {
141        // Test comprehensive error types
142        let config_error = TailwindError::config("Invalid configuration");
143        assert!(matches!(config_error, TailwindError::Config { .. }));
144        assert!(config_error.to_string().contains("Configuration error"));
145
146        let theme_error = TailwindError::theme("Theme not found");
147        assert!(matches!(theme_error, TailwindError::Theme { .. }));
148        assert!(theme_error.to_string().contains("Theme error"));
149
150        let class_error = TailwindError::class_generation("Invalid class name");
151        assert!(matches!(class_error, TailwindError::ClassGeneration { .. }));
152        assert!(class_error.to_string().contains("Class generation error"));
153
154        let build_error = TailwindError::build("Build failed");
155        assert!(matches!(build_error, TailwindError::Build { .. }));
156        assert!(build_error.to_string().contains("Build error"));
157
158        let validation_error = TailwindError::validation("Invalid input");
159        assert!(matches!(validation_error, TailwindError::Validation { .. }));
160        assert!(validation_error.to_string().contains("Validation error"));
161
162        // Test error recovery - errors can be converted back to Result types
163        let result: Result<()> = Err(config_error);
164        assert!(result.is_err());
165
166        // Test error reporting - all errors implement Display and Debug
167        let error = TailwindError::config("Test error");
168        let error_string = format!("{}", error);
169        let debug_string = format!("{:?}", error);
170        assert!(!error_string.is_empty());
171        assert!(!debug_string.is_empty());
172
173        // Test error documentation - errors provide meaningful messages
174        assert!(error_string.contains("Configuration error"));
175        assert!(error_string.contains("Test error"));
176
177        // Test error conversion from standard library errors
178        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
179        let converted_error: TailwindError = io_error.into();
180        assert!(matches!(converted_error, TailwindError::Io(_)));
181
182        // Test JSON error conversion
183        let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
184        let json_tailwind_error: TailwindError = json_error.into();
185        assert!(matches!(json_tailwind_error, TailwindError::Json(_)));
186
187        // Test anyhow error conversion
188        let anyhow_error = anyhow::anyhow!("Generic error");
189        let anyhow_tailwind_error: TailwindError = anyhow_error.into();
190        assert!(matches!(anyhow_tailwind_error, TailwindError::Generic(_)));
191    }
192}
193
194#[cfg(feature = "postcss")]
195impl From<tailwind_rs_postcss::PostCSSError> for TailwindError {
196    fn from(err: tailwind_rs_postcss::PostCSSError) -> Self {
197        TailwindError::Generic(anyhow::anyhow!(err))
198    }
199}