1use std::error::Error as StdError;
82use std::fmt;
83
84#[derive(Debug)]
86pub struct Error {
87 message: String,
88 context: Option<String>,
89 source: Option<Box<dyn StdError + Send + Sync>>,
90}
91
92impl Error {
93 #[must_use]
95 pub fn new(message: &str) -> Self {
96 Self {
97 message: message.to_string(),
98 context: None,
99 source: None,
100 }
101 }
102
103 #[must_use]
105 pub fn new_fmt(args: std::fmt::Arguments) -> Self {
106 Self {
107 message: args.to_string(),
108 context: None,
109 source: None,
110 }
111 }
112
113 #[must_use]
115 pub fn with_context(message: &str, context: &str) -> Self {
116 Self {
117 message: message.to_string(),
118 context: Some(context.to_string()),
119 source: None,
120 }
121 }
122
123 #[must_use]
125 pub fn with_source(message: &str, source: Box<dyn StdError + Send + Sync>) -> Self {
126 Self {
127 message: message.to_string(),
128 context: None,
129 source: Some(source),
130 }
131 }
132
133 #[must_use]
136 pub fn context<C>(self, context: C) -> Self
137 where
138 C: fmt::Display + Send + Sync + 'static,
139 {
140 Self {
141 message: context.to_string(),
142 context: None,
143 source: Some(Box::new(self)),
144 }
145 }
146
147 #[must_use]
150 pub fn with_context_fn<C, F>(self, f: F) -> Self
151 where
152 C: fmt::Display + Send + Sync + 'static,
153 F: FnOnce() -> C,
154 {
155 Self {
156 message: f().to_string(),
157 context: None,
158 source: Some(Box::new(self)),
159 }
160 }
161}
162
163impl fmt::Display for Error {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "{}", self.message)?;
166
167 if let Some(context) = &self.context {
168 write!(f, " (context: {context})")?;
169 }
170
171 if let Some(source) = &self.source {
172 write!(f, " (caused by: {source})")?;
173 }
174
175 Ok(())
176 }
177}
178
179impl StdError for Error {
180 fn source(&self) -> Option<&(dyn StdError + 'static)> {
181 self.source
182 .as_ref()
183 .map(|s| s.as_ref() as &(dyn StdError + 'static))
184 }
185}
186
187pub type Result<T> = std::result::Result<T, Error>;
189
190impl From<std::io::Error> for Error {
192 fn from(err: std::io::Error) -> Self {
193 Self::new(&err.to_string())
194 }
195}
196
197impl From<serde_yaml::Error> for Error {
198 fn from(err: serde_yaml::Error) -> Self {
199 Self::new(&err.to_string())
200 }
201}
202
203impl From<serde_json::Error> for Error {
204 fn from(err: serde_json::Error) -> Self {
205 Self::new(&err.to_string())
206 }
207}
208
209impl From<tera::Error> for Error {
210 fn from(err: tera::Error) -> Self {
211 Self::new(&err.to_string())
212 }
213}
214
215impl From<config::ConfigError> for Error {
216 fn from(err: config::ConfigError) -> Self {
217 Self::new(&err.to_string())
218 }
219}
220
221impl From<log::SetLoggerError> for Error {
222 fn from(err: log::SetLoggerError) -> Self {
223 Self::new(&err.to_string())
224 }
225}
226
227impl<T> From<std::sync::PoisonError<T>> for Error {
228 fn from(err: std::sync::PoisonError<T>) -> Self {
229 Self::new(&err.to_string())
230 }
231}
232
233impl From<anyhow::Error> for Error {
234 fn from(err: anyhow::Error) -> Self {
235 Self::new(&err.to_string())
236 }
237}
238
239impl From<toml::de::Error> for Error {
240 fn from(err: toml::de::Error) -> Self {
241 Self::new(&err.to_string())
242 }
243}
244
245impl From<String> for Error {
246 fn from(err: String) -> Self {
247 Self::new(&err)
248 }
249}
250
251impl From<&str> for Error {
252 fn from(err: &str) -> Self {
253 Self::new(err)
254 }
255}
256
257impl From<oxigraph::store::StorageError> for Error {
259 fn from(err: oxigraph::store::StorageError) -> Self {
260 Self::new(&err.to_string())
261 }
262}
263
264impl From<oxigraph::sparql::QueryEvaluationError> for Error {
265 fn from(err: oxigraph::sparql::QueryEvaluationError) -> Self {
266 Self::new(&err.to_string())
267 }
268}
269
270impl From<oxigraph::store::SerializerError> for Error {
271 fn from(err: oxigraph::store::SerializerError) -> Self {
272 Self::new(&err.to_string())
273 }
274}
275
276impl From<toml::ser::Error> for Error {
277 fn from(err: toml::ser::Error) -> Self {
278 Self::new(&err.to_string())
279 }
280}
281
282pub type GgenError = Error;
284
285pub trait Context<T> {
287 fn context<C>(self, context: C) -> Result<T>
289 where
290 C: fmt::Display + Send + Sync + 'static;
291
292 fn with_context<C, F>(self, f: F) -> Result<T>
294 where
295 C: fmt::Display + Send + Sync + 'static,
296 F: FnOnce() -> C;
297}
298
299impl<T> Context<T> for Result<T> {
300 fn context<C>(self, context: C) -> Result<T>
301 where
302 C: fmt::Display + Send + Sync + 'static,
303 {
304 self.map_err(|e| e.context(context))
305 }
306
307 fn with_context<C, F>(self, f: F) -> Result<T>
308 where
309 C: fmt::Display + Send + Sync + 'static,
310 F: FnOnce() -> C,
311 {
312 self.map_err(|e| e.with_context_fn(f))
313 }
314}
315
316#[macro_export]
334macro_rules! bail {
335 ($msg:literal $(,)?) => {
336 return Err($crate::error::Error::new($msg));
337 };
338 ($fmt:expr, $($arg:tt)*) => {
339 return Err($crate::error::Error::new(&format!($fmt, $($arg)*)));
340 };
341}
342
343#[macro_export]
359macro_rules! ensure {
360 ($condition:expr, $msg:literal $(,)?) => {
361 if !$condition {
362 $crate::bail!($msg);
363 }
364 };
365 ($condition:expr, $fmt:expr, $($arg:tt)*) => {
366 if !$condition {
367 $crate::bail!($fmt, $($arg)*);
368 }
369 };
370}
371
372impl Error {
373 #[must_use]
375 pub fn invalid_input(message: impl Into<String>) -> Self {
376 let msg = message.into();
377 Self::new(&format!("Invalid input: {}", msg))
378 }
379
380 #[must_use]
382 pub fn network_error(message: impl Into<String>) -> Self {
383 let msg = message.into();
384 Self::new(&format!("Network error: {}", msg))
385 }
386
387 #[must_use]
389 pub fn feature_not_enabled(feature: &str, help: &str) -> Self {
390 Self::new(&format!("Feature '{}' not enabled. {}", feature, help))
391 }
392
393 #[must_use]
395 pub fn file_not_found(path: std::path::PathBuf) -> Self {
396 Self::new(&format!("File not found: {}", path.display()))
397 }
398
399 #[must_use]
401 pub fn io_error(message: impl Into<String>) -> Self {
402 let msg = message.into();
403 Self::new(&format!("IO error: {}", msg))
404 }
405
406 #[must_use]
408 pub fn internal_error(message: impl Into<String>) -> Self {
409 let msg = message.into();
410 Self::new(&format!("Internal error: {}", msg))
411 }
412
413 #[must_use]
415 pub fn invalid_state(message: impl Into<String>) -> Self {
416 let msg = message.into();
417 Self::new(&format!("Invalid state: {}", msg))
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424
425 #[test]
426 fn test_error_creation() {
427 let error = Error::new("Test error message");
428 assert_eq!(error.message, "Test error message");
429 }
430
431 #[test]
432 fn test_error_display() {
433 let error = Error::new("Test error message");
434 let display = format!("{error}");
435 assert_eq!(display, "Test error message");
436 }
437
438 #[test]
439 fn test_error_debug() {
440 let error = Error::new("Test error message");
441 let debug = format!("{error:?}");
442 assert!(debug.contains("Test error message"));
443 }
444
445 #[test]
446 fn test_error_from_io_error() {
447 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
448 let error: Error = io_error.into();
449
450 assert!(error.to_string().contains("File not found"));
451 }
452
453 #[test]
454 fn test_error_from_yaml_error() {
455 let yaml_content = "invalid: yaml: content: [";
456 let yaml_error = serde_yaml::from_str::<serde_yaml::Value>(yaml_content).unwrap_err();
457 let error: Error = yaml_error.into();
458
459 assert!(!error.to_string().is_empty());
460 }
461
462 #[test]
463 fn test_error_from_json_error() {
464 let json_content = "invalid json content";
465 let json_error = serde_json::from_str::<serde_json::Value>(json_content).unwrap_err();
466 let error: Error = json_error.into();
467
468 assert!(!error.to_string().is_empty());
469 }
470
471 #[test]
472 fn test_error_from_tera_error() {
473 let template_content = "{{ invalid template syntax";
474 let tera_error = tera::Tera::new("templates/**/*")
475 .unwrap()
476 .render_str(template_content, &tera::Context::new())
477 .unwrap_err();
478 let error: Error = tera_error.into();
479
480 assert!(!error.to_string().is_empty());
481 }
482
483 #[test]
484 fn test_result_type() {
485 fn success_function() -> String {
486 "success".to_string()
487 }
488
489 fn error_function() -> Result<String> {
490 Err(Error::new("error"))
491 }
492
493 assert_eq!(success_function(), "success");
494
495 assert!(error_function().is_err());
496 assert_eq!(error_function().unwrap_err().to_string(), "error");
497 }
498
499 #[test]
500 fn test_error_chain() {
501 let io_error =
502 std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Permission denied");
503 let error: Error = io_error.into();
504
505 let error_ref: &dyn std::error::Error = &error;
507 assert!(!error_ref.to_string().is_empty());
508 }
509
510 #[test]
511 fn test_context_trait() {
512 let result: Result<()> = Err(Error::new("Original error"));
513 let result_with_context = result.context("Failed to process");
514 assert!(result_with_context.is_err());
515 let err = result_with_context.unwrap_err();
516 assert!(err.to_string().contains("Failed to process"));
517 }
518
519 #[test]
520 fn test_with_context_trait() {
521 let result: Result<()> = Err(Error::new("Original error"));
522 let result_with_context = result.with_context(|| format!("Failed at step {}", 1));
523 assert!(result_with_context.is_err());
524 let err = result_with_context.unwrap_err();
525 assert!(err.to_string().contains("Failed at step 1"));
526 }
527
528 #[test]
529 fn test_error_context_method() {
530 let error = Error::new("Original error");
531 let error_with_context = error.context("Additional context");
532 assert!(error_with_context
533 .to_string()
534 .contains("Additional context"));
535 }
536}