rgen_utils/
error.rs

1use std::error::Error as StdError;
2use std::fmt;
3
4/// Custom error type for the rgen project
5#[derive(Debug)]
6pub struct Error {
7    message: String,
8    context: Option<String>,
9    source: Option<Box<dyn StdError + Send + Sync>>,
10}
11
12impl Error {
13    /// Create a new error with a message
14    pub fn new(message: &str) -> Self {
15        Self {
16            message: message.to_string(),
17            context: None,
18            source: None,
19        }
20    }
21
22    /// Create a new error with a formatted message
23    pub fn new_fmt(args: std::fmt::Arguments) -> Self {
24        Self {
25            message: args.to_string(),
26            context: None,
27            source: None,
28        }
29    }
30
31    /// Create an error with additional context
32    pub fn with_context(message: &str, context: &str) -> Self {
33        Self {
34            message: message.to_string(),
35            context: Some(context.to_string()),
36            source: None,
37        }
38    }
39
40    /// Create an error with a source error
41    pub fn with_source(message: &str, source: Box<dyn StdError + Send + Sync>) -> Self {
42        Self {
43            message: message.to_string(),
44            context: None,
45            source: Some(source),
46        }
47    }
48}
49
50impl fmt::Display for Error {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        write!(f, "{}", self.message)?;
53
54        if let Some(context) = &self.context {
55            write!(f, " (context: {})", context)?;
56        }
57
58        if let Some(source) = &self.source {
59            write!(f, " (caused by: {})", source)?;
60        }
61
62        Ok(())
63    }
64}
65
66impl StdError for Error {
67    fn source(&self) -> Option<&(dyn StdError + 'static)> {
68        self.source
69            .as_ref()
70            .map(|s| s.as_ref() as &(dyn StdError + 'static))
71    }
72}
73
74/// Result type alias for the rgen project
75pub type Result<T> = std::result::Result<T, Error>;
76
77// Implement From for common error types
78impl From<std::io::Error> for Error {
79    fn from(err: std::io::Error) -> Self {
80        Self::new(&err.to_string())
81    }
82}
83
84impl From<serde_yaml::Error> for Error {
85    fn from(err: serde_yaml::Error) -> Self {
86        Self::new(&err.to_string())
87    }
88}
89
90impl From<serde_json::Error> for Error {
91    fn from(err: serde_json::Error) -> Self {
92        Self::new(&err.to_string())
93    }
94}
95
96impl From<tera::Error> for Error {
97    fn from(err: tera::Error) -> Self {
98        Self::new(&err.to_string())
99    }
100}
101
102impl From<config::ConfigError> for Error {
103    fn from(err: config::ConfigError) -> Self {
104        Self::new(&err.to_string())
105    }
106}
107
108impl From<log::SetLoggerError> for Error {
109    fn from(err: log::SetLoggerError) -> Self {
110        Self::new(&err.to_string())
111    }
112}
113
114impl<T> From<std::sync::PoisonError<T>> for Error {
115    fn from(err: std::sync::PoisonError<T>) -> Self {
116        Self::new(&err.to_string())
117    }
118}
119
120impl From<anyhow::Error> for Error {
121    fn from(err: anyhow::Error) -> Self {
122        Self::new(&err.to_string())
123    }
124}
125
126impl From<toml::de::Error> for Error {
127    fn from(err: toml::de::Error) -> Self {
128        Self::new(&err.to_string())
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn test_error_creation() {
138        let error = Error::new("Test error message");
139        assert_eq!(error.message, "Test error message");
140    }
141
142    #[test]
143    fn test_error_display() {
144        let error = Error::new("Test error message");
145        let display = format!("{}", error);
146        assert_eq!(display, "Test error message");
147    }
148
149    #[test]
150    fn test_error_debug() {
151        let error = Error::new("Test error message");
152        let debug = format!("{:?}", error);
153        assert!(debug.contains("Test error message"));
154    }
155
156    #[test]
157    fn test_error_from_io_error() {
158        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
159        let error: Error = io_error.into();
160
161        assert!(error.to_string().contains("File not found"));
162    }
163
164    #[test]
165    fn test_error_from_yaml_error() {
166        let yaml_content = "invalid: yaml: content: [";
167        let yaml_error = serde_yaml::from_str::<serde_yaml::Value>(yaml_content).unwrap_err();
168        let error: Error = yaml_error.into();
169
170        assert!(!error.to_string().is_empty());
171    }
172
173    #[test]
174    fn test_error_from_json_error() {
175        let json_content = "invalid json content";
176        let json_error = serde_json::from_str::<serde_json::Value>(json_content).unwrap_err();
177        let error: Error = json_error.into();
178
179        assert!(!error.to_string().is_empty());
180    }
181
182    #[test]
183    fn test_error_from_tera_error() {
184        let template_content = "{{ invalid template syntax";
185        let tera_error = tera::Tera::new("templates/**/*")
186            .unwrap()
187            .render_str(template_content, &tera::Context::new())
188            .unwrap_err();
189        let error: Error = tera_error.into();
190
191        assert!(!error.to_string().is_empty());
192    }
193
194    #[test]
195    fn test_result_type() {
196        fn success_function() -> Result<String> {
197            Ok("success".to_string())
198        }
199
200        fn error_function() -> Result<String> {
201            Err(Error::new("error"))
202        }
203
204        assert!(success_function().is_ok());
205        assert_eq!(success_function().unwrap(), "success");
206
207        assert!(error_function().is_err());
208        assert_eq!(error_function().unwrap_err().to_string(), "error");
209    }
210
211    #[test]
212    fn test_error_chain() {
213        let io_error =
214            std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Permission denied");
215        let error: Error = io_error.into();
216
217        // Test that the error can be used as std::error::Error
218        let error_ref: &dyn std::error::Error = &error;
219        assert!(!error_ref.to_string().is_empty());
220    }
221}