1use std::error::Error as StdError;
2use std::fmt;
3
4#[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 pub fn new(message: &str) -> Self {
15 Self {
16 message: message.to_string(),
17 context: None,
18 source: None,
19 }
20 }
21
22 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 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 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
74pub type Result<T> = std::result::Result<T, Error>;
76
77impl 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 let error_ref: &dyn std::error::Error = &error;
219 assert!(!error_ref.to_string().is_empty());
220 }
221}