Skip to main content

oxigdal_core/error/
extensions.rs

1//! Error handling extensions and utilities
2
3use super::types::OxiGdalError;
4
5#[cfg(not(feature = "std"))]
6use alloc::string::String;
7#[cfg(not(feature = "std"))]
8use alloc::vec::Vec;
9
10/// Macro for precondition checking with automatic error creation
11///
12/// # Examples
13///
14/// ```ignore
15/// use oxigdal_core::ensure;
16/// use oxigdal_core::error::{OxiGdalError, Result};
17///
18/// fn process_data(size: usize) -> Result<()> {
19///     ensure!(size > 0, InvalidParameter {
20///         parameter: "size",
21///         message: "size must be positive".to_string()
22///     });
23///     Ok(())
24/// }
25/// ```
26#[macro_export]
27macro_rules! ensure {
28    ($cond:expr, $err:expr) => {
29        if !($cond) {
30            return Err($crate::error::OxiGdalError::$err.into());
31        }
32    };
33    ($cond:expr, $err:ident { $($field:ident: $value:expr),* $(,)? }) => {
34        if !($cond) {
35            return Err($crate::error::OxiGdalError::$err {
36                $($field: $value),*
37            }.into());
38        }
39    };
40}
41
42/// Trait for adding context to errors
43pub trait ResultExt<T> {
44    /// Add context to an error
45    fn context(self, msg: impl Into<String>) -> crate::error::Result<T>;
46
47    /// Add context with a lazy message (only evaluated on error)
48    fn with_context<F>(self, f: F) -> crate::error::Result<T>
49    where
50        F: FnOnce() -> String;
51}
52
53impl<T> ResultExt<T> for crate::error::Result<T> {
54    fn context(self, msg: impl Into<String>) -> crate::error::Result<T> {
55        self.map_err(|_| OxiGdalError::Internal {
56            message: msg.into(),
57        })
58    }
59
60    fn with_context<F>(self, f: F) -> crate::error::Result<T>
61    where
62        F: FnOnce() -> String,
63    {
64        self.map_err(|_| OxiGdalError::Internal { message: f() })
65    }
66}
67
68/// Aggregate multiple errors into a single error
69#[derive(Debug)]
70pub struct ErrorAggregator {
71    errors: Vec<OxiGdalError>,
72}
73
74impl ErrorAggregator {
75    /// Create a new error aggregator
76    pub fn new() -> Self {
77        Self { errors: Vec::new() }
78    }
79
80    /// Add an error to the aggregator
81    pub fn add(&mut self, error: OxiGdalError) {
82        self.errors.push(error);
83    }
84
85    /// Add a result, collecting any errors
86    pub fn add_result<T>(&mut self, result: crate::error::Result<T>) -> Option<T> {
87        match result {
88            Ok(value) => Some(value),
89            Err(error) => {
90                self.add(error);
91                None
92            }
93        }
94    }
95
96    /// Check if any errors were collected
97    pub fn has_errors(&self) -> bool {
98        !self.errors.is_empty()
99    }
100
101    /// Get the number of errors collected
102    pub fn count(&self) -> usize {
103        self.errors.len()
104    }
105
106    /// Convert to a result, failing if any errors were collected
107    pub fn into_result(self) -> crate::error::Result<()> {
108        if self.errors.is_empty() {
109            Ok(())
110        } else {
111            let count = self.errors.len();
112            let first = &self.errors[0];
113            Err(OxiGdalError::Internal {
114                message: format!(
115                    "Multiple errors occurred ({} total). First error: {}",
116                    count, first
117                ),
118            })
119        }
120    }
121
122    /// Get all collected errors
123    pub fn into_errors(self) -> Vec<OxiGdalError> {
124        self.errors
125    }
126}
127
128impl Default for ErrorAggregator {
129    fn default() -> Self {
130        Self::new()
131    }
132}