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]
335macro_rules! bail {
336 ($msg:literal $(,)?) => {
337 return Err($crate::utils::error::Error::new($msg))
338 };
339 ($fmt:expr, $($arg:tt)*) => {
340 return Err($crate::utils::error::Error::new(&format!($fmt, $($arg)*)))
341 };
342}
343
344#[macro_export]
361macro_rules! ensure {
362 ($condition:expr, $msg:literal $(,)?) => {
363 if !$condition {
364 $crate::bail!($msg);
365 }
366 };
367 ($condition:expr, $fmt:expr, $($arg:tt)*) => {
368 if !$condition {
369 $crate::bail!($fmt, $($arg)*);
370 }
371 };
372}
373
374#[macro_export]
376macro_rules! ggen_error {
377 ($msg:literal $(,)?) => {
378 $crate::utils::error::Error::new($msg)
379 };
380 ($fmt:expr, $($arg:tt)*) => {
381 $crate::utils::error::Error::new(&format!($fmt, $($arg)*))
382 };
383}
384
385pub use crate::ggen_error;
386
387impl Error {
388 #[must_use]
390 pub fn invalid_input(message: impl Into<String>) -> Self {
391 let msg = message.into();
392 Self::new(&format!("Invalid input: {}", msg))
393 }
394
395 #[must_use]
397 pub fn network_error(message: impl Into<String>) -> Self {
398 let msg = message.into();
399 Self::new(&format!("Network error: {}", msg))
400 }
401
402 #[must_use]
404 pub fn feature_not_enabled(feature: &str, help: &str) -> Self {
405 Self::new(&format!("Feature '{}' not enabled. {}", feature, help))
406 }
407
408 #[must_use]
410 pub fn file_not_found(path: std::path::PathBuf) -> Self {
411 Self::new(&format!("File not found: {}", path.display()))
412 }
413
414 #[must_use]
416 pub fn io_error(message: impl Into<String>) -> Self {
417 let msg = message.into();
418 Self::new(&format!("IO error: {}", msg))
419 }
420
421 #[must_use]
423 pub fn internal_error(message: impl Into<String>) -> Self {
424 let msg = message.into();
425 Self::new(&format!("Internal error: {}", msg))
426 }
427
428 #[must_use]
430 pub fn invalid_state(message: impl Into<String>) -> Self {
431 let msg = message.into();
432 Self::new(&format!("Invalid state: {}", msg))
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 #[test]
441 fn test_error_creation() {
442 let error = Error::new("Test error message");
443 assert_eq!(error.message, "Test error message");
444 }
445
446 #[test]
447 fn test_error_display() {
448 let error = Error::new("Test error message");
449 let display = format!("{error}");
450 assert_eq!(display, "Test error message");
451 }
452
453 #[test]
454 fn test_error_debug() {
455 let error = Error::new("Test error message");
456 let debug = format!("{error:?}");
457 assert!(debug.contains("Test error message"));
458 }
459
460 #[test]
461 fn test_error_from_io_error_old() {
462 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
463 let error: Error = io_error.into();
464
465 assert!(error.to_string().contains("File not found"));
466 }
467
468 #[test]
469 fn test_error_from_yaml_error() {
470 let yaml_content = "invalid: yaml: content: [";
471 let yaml_error = serde_yaml::from_str::<serde_yaml::Value>(yaml_content).unwrap_err();
472 let error: Error = yaml_error.into();
473
474 assert!(!error.to_string().is_empty());
475 }
476
477 #[test]
478 fn test_error_from_json_error() {
479 let json_content = "invalid json content";
480 let json_error = serde_json::from_str::<serde_json::Value>(json_content).unwrap_err();
481 let error: Error = json_error.into();
482
483 assert!(!error.to_string().is_empty());
484 }
485
486 #[test]
487 #[ignore]
488 fn test_error_from_tera_error() {
489 let template_content = "{{ invalid template syntax";
490 let tera_error = tera::Tera::new("templates/**/*")
491 .unwrap()
492 .render_str(template_content, &tera::Context::new())
493 .unwrap_err();
494 let error: Error = tera_error.into();
495
496 assert!(!error.to_string().is_empty());
497 }
498
499 #[test]
500 fn test_result_type() {
501 fn success_function() -> String {
502 "success".to_string()
503 }
504
505 fn error_function() -> Result<String> {
506 Err(Error::new("error"))
507 }
508
509 assert_eq!(success_function(), "success");
510
511 assert!(error_function().is_err());
512 assert_eq!(error_function().unwrap_err().to_string(), "error");
513 }
514
515 #[test]
516 fn test_error_chain() {
517 let io_error =
518 std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Permission denied");
519 let error: Error = io_error.into();
520
521 let error_ref: &dyn std::error::Error = &error;
523 assert!(!error_ref.to_string().is_empty());
524 }
525
526 #[test]
527 fn test_context_trait() {
528 let result: Result<()> = Err(Error::new("Original error"));
529 let result_with_context = result.context("Failed to process");
530 assert!(result_with_context.is_err());
531 let err = result_with_context.unwrap_err();
532 assert!(err.to_string().contains("Failed to process"));
533 }
534
535 #[test]
536 fn test_with_context_trait() {
537 let result: Result<()> = Err(Error::new("Original error"));
538 let result_with_context = result.with_context(|| format!("Failed at step {}", 1));
539 assert!(result_with_context.is_err());
540 let err = result_with_context.unwrap_err();
541 assert!(err.to_string().contains("Failed at step 1"));
542 }
543
544 #[test]
545 fn test_error_context_method() {
546 let error = Error::new("Original error");
547 let error_with_context = error.context("Additional context");
548 assert!(error_with_context
549 .to_string()
550 .contains("Additional context"));
551 }
552}