Skip to main content

scirs2_core/error/
error.rs

1//! Error types for the ``SciRS2`` core module
2//!
3//! This module provides common error types used throughout the ``SciRS2`` ecosystem.
4
5use std::fmt;
6use thiserror::Error;
7
8/// Location information for error context
9#[derive(Debug, Clone)]
10pub struct ErrorLocation {
11    /// File where the error occurred
12    pub file: &'static str,
13    /// Line number where the error occurred
14    pub line: u32,
15    /// Column number where the error occurred
16    pub column: Option<u32>,
17    /// Function where the error occurred
18    pub function: Option<&'static str>,
19}
20
21impl ErrorLocation {
22    /// Create a new error location
23    #[must_use]
24    #[inline]
25    pub const fn new(file: &'static str, line: u32) -> Self {
26        Self {
27            file,
28            line,
29            column: None,
30            function: None,
31        }
32    }
33
34    /// Create a new error location with function information
35    #[must_use]
36    #[inline]
37    pub const fn new_with_function(file: &'static str, line: u32, function: &'static str) -> Self {
38        Self {
39            file,
40            line,
41            column: None,
42            function: Some(function),
43        }
44    }
45
46    /// Create a new error location with column information
47    #[must_use]
48    #[inline]
49    pub const fn new_with_column(file: &'static str, line: u32, column: u32) -> Self {
50        Self {
51            file,
52            line,
53            column: Some(column),
54            function: None,
55        }
56    }
57
58    /// Create a new error location with function and column information
59    #[must_use]
60    #[inline]
61    pub const fn new_full(
62        file: &'static str,
63        line: u32,
64        column: u32,
65        function: &'static str,
66    ) -> Self {
67        Self {
68            file,
69            line,
70            column: Some(column),
71            function: Some(function),
72        }
73    }
74
75    /// Create an error location for the current position (convenience method)
76    #[must_use]
77    #[inline]
78    pub fn here() -> Self {
79        Self::new(file!(), line!())
80    }
81}
82
83impl fmt::Display for ErrorLocation {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(f, "{}:{}", self.file, self.line)?;
86        if let Some(column) = self.column {
87            write!(f, ":{column}")?;
88        }
89        if let Some(function) = self.function {
90            write!(f, " in {function}")?;
91        }
92        Ok(())
93    }
94}
95
96/// Error context containing additional information about an error
97#[derive(Debug, Clone)]
98pub struct ErrorContext {
99    /// Error message
100    pub message: String,
101    /// Location where the error occurred
102    pub location: Option<ErrorLocation>,
103    /// Cause of the error
104    pub cause: Option<Box<CoreError>>,
105}
106
107impl ErrorContext {
108    /// Create a new error context
109    #[must_use]
110    pub fn new<S: Into<String>>(message: S) -> Self {
111        Self {
112            message: message.into(),
113            location: None,
114            cause: None,
115        }
116    }
117
118    /// Add location information to the error context
119    #[must_use]
120    pub fn with_location(mut self, location: ErrorLocation) -> Self {
121        self.location = Some(location);
122        self
123    }
124
125    /// Add a cause to the error context
126    #[must_use]
127    pub fn with_cause(mut self, cause: CoreError) -> Self {
128        self.cause = Some(Box::new(cause));
129        self
130    }
131}
132
133impl fmt::Display for ErrorContext {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        write!(f, "{}", self.message)?;
136        if let Some(location) = &self.location {
137            write!(f, " at {location}")?;
138        }
139        if let Some(cause) = &self.cause {
140            write!(f, "\nCaused by: {cause}")?;
141        }
142        Ok(())
143    }
144}
145
146/// Core error type for ``SciRS2``
147///
148/// This enum is `#[non_exhaustive]`: downstream crates must include a wildcard
149/// `_` arm when matching on it, so that adding new variants here is not a
150/// breaking change.  The `core_error_non_exhaustive` compile-fail test in
151/// `scirs2-stability-tests` guards this contract.
152#[derive(Error, Debug, Clone)]
153#[non_exhaustive]
154pub enum CoreError {
155    /// Computation error (generic error)
156    #[error("{0}")]
157    ComputationError(ErrorContext),
158
159    /// Domain error (input outside valid domain)
160    #[error("{0}")]
161    DomainError(ErrorContext),
162
163    /// Dispatch error (array protocol dispatch failed)
164    #[error("{0}")]
165    DispatchError(ErrorContext),
166
167    /// Convergence error (algorithm did not converge)
168    #[error("{0}")]
169    ConvergenceError(ErrorContext),
170
171    /// Dimension mismatch error
172    #[error("{0}")]
173    DimensionError(ErrorContext),
174
175    /// Shape error (matrices/arrays have incompatible shapes)
176    #[error("{0}")]
177    ShapeError(ErrorContext),
178
179    /// Out of bounds error
180    #[error("{0}")]
181    IndexError(ErrorContext),
182
183    /// Value error (invalid value)
184    #[error("{0}")]
185    ValueError(ErrorContext),
186
187    /// Type error (invalid type)
188    #[error("{0}")]
189    TypeError(ErrorContext),
190
191    /// Not implemented error
192    #[error("{0}")]
193    NotImplementedError(ErrorContext),
194
195    /// Implementation error (method exists but not fully implemented yet)
196    #[error("{0}")]
197    ImplementationError(ErrorContext),
198
199    /// Memory error (could not allocate memory)
200    #[error("{0}")]
201    MemoryError(ErrorContext),
202
203    /// Allocation error (memory allocation failed)
204    #[error("{0}")]
205    AllocationError(ErrorContext),
206
207    /// Configuration error (invalid configuration)
208    #[error("{0}")]
209    ConfigError(ErrorContext),
210
211    /// Invalid argument error
212    #[error("{0}")]
213    InvalidArgument(ErrorContext),
214
215    /// Invalid input error
216    #[error("{0}")]
217    InvalidInput(ErrorContext),
218
219    /// Permission error (insufficient permissions)
220    #[error("{0}")]
221    PermissionError(ErrorContext),
222
223    /// Validation error (input failed validation)
224    #[error("{0}")]
225    ValidationError(ErrorContext),
226
227    /// Invalid state error (object is in an invalid state)
228    #[error("{0}")]
229    InvalidState(ErrorContext),
230
231    /// JIT compilation error (error during JIT compilation)
232    #[error("{0}")]
233    JITError(ErrorContext),
234
235    /// JSON error
236    #[error("JSON error: {0}")]
237    JSONError(ErrorContext),
238
239    /// IO error
240    #[error("IO error: {0}")]
241    IoError(ErrorContext),
242
243    /// Scheduler error (error in work-stealing scheduler)
244    #[error("Scheduler error: {0}")]
245    SchedulerError(ErrorContext),
246
247    /// Timeout error (operation timed out)
248    #[error("Timeout error: {0}")]
249    TimeoutError(ErrorContext),
250
251    /// Compression error (error during compression/decompression)
252    #[error("Compression error: {0}")]
253    CompressionError(ErrorContext),
254
255    /// Invalid shape error (array shape is invalid)
256    #[error("Invalid shape: {0}")]
257    InvalidShape(ErrorContext),
258
259    /// Device error (GPU/hardware device error)
260    #[error("Device error: {0}")]
261    DeviceError(ErrorContext),
262
263    /// Mutex error (mutex poisoning or lock error)
264    #[error("Mutex error: {0}")]
265    MutexError(ErrorContext),
266
267    /// Thread error (threading error)
268    #[error("Thread error: {0}")]
269    ThreadError(ErrorContext),
270
271    /// Stream error (streaming operation error)
272    #[error("Stream error: {0}")]
273    StreamError(ErrorContext),
274
275    /// End of stream error (stream ended unexpectedly)
276    #[error("End of stream: {0}")]
277    EndOfStream(ErrorContext),
278
279    /// Resource error (insufficient or unavailable resources)
280    #[error("Resource error: {0}")]
281    ResourceError(ErrorContext),
282
283    /// Communication error (network or inter-process communication error)
284    #[error("Communication error: {0}")]
285    CommunicationError(ErrorContext),
286
287    /// Security error (authentication, authorization, or security-related error)
288    #[error("Security error: {0}")]
289    SecurityError(ErrorContext),
290}
291
292/// Result type alias for core operations
293pub type CoreResult<T> = Result<T, CoreError>;
294
295/// Convert from std::io::Error to CoreError
296impl From<std::io::Error> for CoreError {
297    fn from(err: std::io::Error) -> Self {
298        CoreError::IoError(ErrorContext::new(format!("IO error: {err}")))
299    }
300}
301
302/// Convert from serde_json::Error to CoreError
303#[cfg(feature = "serialization")]
304impl From<serde_json::Error> for CoreError {
305    fn from(err: serde_json::Error) -> Self {
306        CoreError::JSONError(ErrorContext::new(format!("JSON error: {err}")))
307    }
308}
309
310/// Convert from String to CoreError (for parsing errors)
311impl From<String> for CoreError {
312    fn from(err: String) -> Self {
313        CoreError::ValueError(ErrorContext::new(err))
314    }
315}
316
317/// Convert from OperationError to CoreError
318impl From<crate::array_protocol::OperationError> for CoreError {
319    fn from(err: crate::array_protocol::OperationError) -> Self {
320        use crate::array_protocol::OperationError;
321        match err {
322            // Preserving NotImplemented for compatibility with older code,
323            // but it will eventually be replaced with NotImplementedError
324            OperationError::NotImplemented(msg) => {
325                CoreError::NotImplementedError(ErrorContext::new(msg))
326            }
327            OperationError::ShapeMismatch(msg) => CoreError::ShapeError(ErrorContext::new(msg)),
328            OperationError::TypeMismatch(msg) => CoreError::TypeError(ErrorContext::new(msg)),
329            OperationError::Other(msg) => CoreError::ComputationError(ErrorContext::new(msg)),
330        }
331    }
332}
333
334/// Macro to create a new error context with location information
335///
336/// # Example
337///
338/// ```rust
339/// use scirs2_core::error_context;
340/// use scirs2_core::error::{CoreResult, CoreError};
341///
342/// fn example() -> CoreResult<()> {
343///     let condition = false;
344///     if condition {
345///         return Err(CoreError::ComputationError(error_context!("An error occurred")));
346///     }
347///     Ok(())
348/// }
349/// ```
350#[macro_export]
351macro_rules! error_context {
352    ($message:expr) => {
353        $crate::error::ErrorContext::new($message)
354            .with_location($crate::error::ErrorLocation::new(file!(), line!()))
355    };
356    ($message:expr, $function:expr) => {
357        $crate::error::ErrorContext::new($message).with_location(
358            $crate::error::ErrorLocation::new_with_function(file!(), line!(), $function),
359        )
360    };
361}
362
363/// Macro to create a domain error with location information
364#[macro_export]
365macro_rules! domainerror {
366    ($message:expr) => {
367        $crate::error::CoreError::DomainError(error_context!($message))
368    };
369    ($message:expr, $function:expr) => {
370        $crate::error::CoreError::DomainError(error_context!($message, $function))
371    };
372}
373
374/// Macro to create a dimension error with location information
375#[macro_export]
376macro_rules! dimensionerror {
377    ($message:expr) => {
378        $crate::error::CoreError::DimensionError(error_context!($message))
379    };
380    ($message:expr, $function:expr) => {
381        $crate::error::CoreError::DimensionError(error_context!($message, $function))
382    };
383}
384
385/// Macro to create a value error with location information
386#[macro_export]
387macro_rules! valueerror {
388    ($message:expr) => {
389        $crate::error::CoreError::ValueError(error_context!($message))
390    };
391    ($message:expr, $function:expr) => {
392        $crate::error::CoreError::ValueError(error_context!($message, $function))
393    };
394}
395
396/// Macro to create a computation error with location information
397#[macro_export]
398macro_rules! computationerror {
399    ($message:expr) => {
400        $crate::error::CoreError::ComputationError(error_context!($message))
401    };
402    ($message:expr, $function:expr) => {
403        $crate::error::CoreError::ComputationError(error_context!($message, $function))
404    };
405}
406
407/// Checks if a condition is true, otherwise returns a domain error
408///
409/// # Arguments
410///
411/// * `condition` - The condition to check
412/// * `message` - The error message if the condition is false
413///
414/// # Returns
415///
416/// * `Ok(())` if the condition is true
417/// * `Err(CoreError::DomainError)` if the condition is false
418///
419/// # Errors
420///
421/// Returns `CoreError::DomainError` if the condition is false.
422#[allow(dead_code)]
423pub fn check_domain<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
424    if condition {
425        Ok(())
426    } else {
427        Err(CoreError::DomainError(
428            ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
429        ))
430    }
431}
432
433/// Checks dimensions
434///
435/// # Arguments
436///
437/// * `condition` - The condition to check
438/// * `message` - The error message if the condition is false
439///
440/// # Returns
441///
442/// * `Ok(())` if the condition is true
443/// * `Err(CoreError::DimensionError)` if the condition is false
444///
445/// # Errors
446///
447/// Returns `CoreError::DimensionError` if the condition is false.
448#[allow(dead_code)]
449pub fn check_dimensions<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
450    if condition {
451        Ok(())
452    } else {
453        Err(CoreError::DimensionError(
454            ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
455        ))
456    }
457}
458
459/// Checks if a value is valid
460///
461/// # Arguments
462///
463/// * `condition` - The condition to check
464/// * `message` - The error message if the condition is false
465///
466/// # Returns
467///
468/// * `Ok(())` if the condition is true
469/// * `Err(CoreError::ValueError)` if the condition is false
470///
471/// # Errors
472///
473/// Returns `CoreError::ValueError` if the condition is false.
474#[allow(dead_code)]
475pub fn check_value<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
476    if condition {
477        Ok(())
478    } else {
479        Err(CoreError::ValueError(
480            ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
481        ))
482    }
483}
484
485/// Checks if a value is valid according to a validator function
486///
487/// # Arguments
488///
489/// * `value` - The value to validate
490/// * `validator` - A function that returns true if the value is valid
491/// * `message` - The error message if the value is invalid
492///
493/// # Returns
494///
495/// * `Ok(value)` if the value is valid
496/// * `Err(CoreError::ValidationError)` if the value is invalid
497///
498/// # Errors
499///
500/// Returns `CoreError::ValidationError` if the validator function returns false.
501#[allow(dead_code)]
502pub fn validate<T, F, S>(value: T, validator: F, message: S) -> CoreResult<T>
503where
504    F: FnOnce(&T) -> bool,
505    S: Into<String>,
506{
507    if validator(&value) {
508        Ok(value)
509    } else {
510        Err(CoreError::ValidationError(
511            ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
512        ))
513    }
514}
515
516/// Convert an error from one type to a CoreError
517///
518/// # Arguments
519///
520/// * `error` - The error to convert
521/// * `message` - A message describing the context of the error
522///
523/// # Returns
524///
525/// * A CoreError with the original error as its cause
526#[must_use]
527#[allow(dead_code)]
528pub fn converterror<E, S>(error: E, message: S) -> CoreError
529where
530    E: std::error::Error + 'static,
531    S: Into<String>,
532{
533    // Create a computation error that contains the original error
534    // We combine the provided message with the error's own message for extra context
535    let message_str = message.into();
536    let error_message = format!("{message_str} | Original error: {error}");
537
538    // For I/O errors we have direct conversion via From trait implementation
539    // but we can't use it directly due to the generic bounds.
540    // In a real implementation, you would use a match or if statement with
541    // type_id or another approach to distinguish error types.
542
543    // For simplicity, we'll just use ComputationError as a general case
544    CoreError::ComputationError(
545        ErrorContext::new(error_message).with_location(ErrorLocation::new(file!(), line!())),
546    )
547}
548
549/// Create an error chain by adding a new error context
550///
551/// # Arguments
552///
553/// * `error` - The error to chain
554/// * `message` - A message describing the context of the error
555///
556/// # Returns
557///
558/// * A CoreError with the original error as its cause
559#[must_use]
560#[allow(dead_code)]
561pub fn chainerror<S>(error: CoreError, message: S) -> CoreError
562where
563    S: Into<String>,
564{
565    CoreError::ComputationError(
566        ErrorContext::new(message)
567            .with_location(ErrorLocation::new(file!(), line!()))
568            .with_cause(error),
569    )
570}
571
572/// Error recovery strategies for robust error handling
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub enum ErrorRecoveryStrategy {
575    /// Fail immediately
576    FailFast,
577    /// Retry the operation with exponential backoff
578    RetryExponential,
579    /// Retry the operation with linear backoff
580    RetryLinear,
581    /// Use a fallback operation
582    Fallback,
583    /// Skip the failed operation and continue
584    Skip,
585    /// Use a default value
586    UseDefault,
587    /// Log the error and continue
588    LogAndContinue,
589}
590
591/// Error severity levels for prioritized error handling
592#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
593pub enum ErrorSeverity {
594    /// Low severity - operation can continue
595    Low,
596    /// Medium severity - operation can continue with warnings
597    Medium,
598    /// High severity - operation should be reconsidered
599    High,
600    /// Critical severity - operation must be stopped
601    Critical,
602}
603
604/// Error handler configuration for systematic error management
605#[derive(Debug, Clone)]
606pub struct ErrorHandlerConfig {
607    /// Maximum number of retry attempts
608    pub max_retries: usize,
609    /// Initial retry delay in milliseconds
610    pub initial_delay_ms: u64,
611    /// Maximum retry delay in milliseconds
612    pub max_delay_ms: u64,
613    /// Whether to log errors
614    pub log_errors: bool,
615    /// Error recovery strategy
616    pub recovery_strategy: ErrorRecoveryStrategy,
617    /// Minimum severity level to handle
618    pub min_severity: ErrorSeverity,
619}
620
621impl Default for ErrorHandlerConfig {
622    fn default() -> Self {
623        Self {
624            max_retries: 3,
625            initial_delay_ms: 100,
626            max_delay_ms: 5000,
627            log_errors: true,
628            recovery_strategy: ErrorRecoveryStrategy::FailFast,
629            min_severity: ErrorSeverity::Low,
630        }
631    }
632}
633
634/// Enhanced error with severity and recovery information
635#[derive(Debug, Clone)]
636pub struct EnhancedError {
637    /// Core error
638    pub error: CoreError,
639    /// Error severity
640    pub severity: ErrorSeverity,
641    /// Suggested recovery strategy
642    pub recovery_strategy: ErrorRecoveryStrategy,
643    /// Error occurrence timestamp
644    pub timestamp: std::time::SystemTime,
645    /// Error category for grouping
646    pub category: Option<String>,
647    /// Error tags for filtering
648    pub tags: Vec<String>,
649}
650
651impl EnhancedError {
652    /// Create a new enhanced error
653    pub fn new(error: CoreError, severity: ErrorSeverity) -> Self {
654        Self {
655            error,
656            severity,
657            recovery_strategy: ErrorRecoveryStrategy::FailFast,
658            timestamp: std::time::SystemTime::now(),
659            category: None,
660            tags: Vec::new(),
661        }
662    }
663
664    /// Set recovery strategy
665    pub fn with_recovery(mut self, strategy: ErrorRecoveryStrategy) -> Self {
666        self.recovery_strategy = strategy;
667        self
668    }
669
670    /// Set error category
671    pub fn with_category(mut self, category: impl Into<String>) -> Self {
672        self.category = Some(category.into());
673        self
674    }
675
676    /// Add error tag
677    pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
678        self.tags.push(tag.into());
679        self
680    }
681
682    /// Check if error has specific tag
683    pub fn has_tag(&self, tag: &str) -> bool {
684        self.tags.iter().any(|t| t == tag)
685    }
686
687    /// Check if error is retryable based on its recovery strategy
688    pub fn is_retryable(&self) -> bool {
689        matches!(
690            self.recovery_strategy,
691            ErrorRecoveryStrategy::RetryExponential | ErrorRecoveryStrategy::RetryLinear
692        )
693    }
694
695    /// Check if error is recoverable
696    pub fn is_recoverable(&self) -> bool {
697        !matches!(self.recovery_strategy, ErrorRecoveryStrategy::FailFast)
698    }
699}
700
701impl fmt::Display for EnhancedError {
702    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703        write!(f, "[{:?}] {}", self.severity, self.error)?;
704        if let Some(category) = &self.category {
705            write!(f, " (category: {category})")?;
706        }
707        if !self.tags.is_empty() {
708            write!(f, " [tags: {}]", self.tags.join(", "))?;
709        }
710        Ok(())
711    }
712}
713
714/// Batch error handling for processing multiple operations
715#[derive(Debug, Clone)]
716pub struct BatchError {
717    /// Individual errors
718    pub errors: Vec<EnhancedError>,
719    /// Total operations attempted
720    pub total_operations: usize,
721    /// Number of successful operations
722    pub successful_operations: usize,
723}
724
725impl BatchError {
726    /// Create a new batch error
727    pub fn new(total_operations: usize) -> Self {
728        Self {
729            errors: Vec::new(),
730            total_operations,
731            successful_operations: 0,
732        }
733    }
734
735    /// Add an error to the batch
736    pub fn add_error(&mut self, error: EnhancedError) {
737        self.errors.push(error);
738    }
739
740    /// Record a successful operation
741    pub fn add_success(&mut self) {
742        self.successful_operations += 1;
743    }
744
745    /// Get error rate (0.0 to 1.0)
746    pub fn error_rate(&self) -> f64 {
747        if self.total_operations == 0 {
748            0.0
749        } else {
750            self.errors.len() as f64 / self.total_operations as f64
751        }
752    }
753
754    /// Get success rate (0.0 to 1.0)
755    pub fn success_rate(&self) -> f64 {
756        if self.total_operations == 0 {
757            0.0
758        } else {
759            self.successful_operations as f64 / self.total_operations as f64
760        }
761    }
762
763    /// Check if batch operation was successful (configurable threshold)
764    pub fn is_successful(&self, min_success_rate: f64) -> bool {
765        self.success_rate() >= min_success_rate
766    }
767
768    /// Get errors by severity
769    pub fn errors_by_severity(&self, severity: ErrorSeverity) -> Vec<&EnhancedError> {
770        self.errors
771            .iter()
772            .filter(|e| e.severity == severity)
773            .collect()
774    }
775
776    /// Get errors by category
777    pub fn errors_by_category(&self, category: &str) -> Vec<&EnhancedError> {
778        self.errors
779            .iter()
780            .filter(|e| e.category.as_deref() == Some(category))
781            .collect()
782    }
783
784    /// Get retryable errors
785    pub fn retryable_errors(&self) -> Vec<&EnhancedError> {
786        self.errors.iter().filter(|e| e.is_retryable()).collect()
787    }
788}
789
790impl fmt::Display for BatchError {
791    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792        write!(
793            f,
794            "Batch operation: {}/{} successful ({:.1}% success rate)",
795            self.successful_operations,
796            self.total_operations,
797            self.success_rate() * 100.0
798        )?;
799        if !self.errors.is_empty() {
800            write!(f, "\nErrors ({})", self.errors.len())?;
801            for (i, error) in self.errors.iter().enumerate() {
802                write!(f, "\n  {}: {}", i + 1, error)?;
803            }
804        }
805        Ok(())
806    }
807}
808
809/// Result type for enhanced error handling
810pub type EnhancedResult<T> = Result<T, EnhancedError>;
811
812/// Result type for batch operations
813pub type BatchResult<T> = Result<T, BatchError>;
814
815/// Convenience function to create an enhanced error from a core error
816pub fn enhance_error(error: CoreError, severity: ErrorSeverity) -> EnhancedError {
817    EnhancedError::new(error, severity)
818}
819
820/// Convenience function to create a critical error
821pub fn critical_error(error: CoreError) -> EnhancedError {
822    EnhancedError::new(error, ErrorSeverity::Critical)
823}
824
825/// Convenience function to create a high severity error
826pub fn high_error(error: CoreError) -> EnhancedError {
827    EnhancedError::new(error, ErrorSeverity::High)
828}
829
830/// Convenience function to create a medium severity error
831pub fn medium_error(error: CoreError) -> EnhancedError {
832    EnhancedError::new(error, ErrorSeverity::Medium)
833}
834
835/// Convenience function to create a low severity error
836pub fn low_error(error: CoreError) -> EnhancedError {
837    EnhancedError::new(error, ErrorSeverity::Low)
838}
839
840/// Macro to create an enhanced error with automatic severity detection
841#[macro_export]
842macro_rules! enhanced_error {
843    (critical, $error:expr) => {
844        $crate::error::critical_error($error)
845    };
846    (high, $error:expr) => {
847        $crate::error::high_error($error)
848    };
849    (medium, $error:expr) => {
850        $crate::error::medium_error($error)
851    };
852    (low, $error:expr) => {
853        $crate::error::low_error($error)
854    };
855    ($error:expr) => {
856        $crate::error::medium_error($error)
857    };
858}
859
860/// Macro for batch error handling
861#[macro_export]
862macro_rules! batch_operation {
863    ($total:expr, $operations:block) => {{
864        let mut batch_error = $crate::error::BatchError::new($total);
865        let result = $operations;
866        if batch_error.errors.is_empty() {
867            Ok(result)
868        } else {
869            Err(batch_error)
870        }
871    }};
872}
873
874/// Macro for error recovery with retry logic
875#[macro_export]
876macro_rules! with_retry {
877    ($operation:expr, $max_retries:expr) => {{
878        let mut attempts = 0;
879        let mut last_error = None;
880
881        loop {
882            match $operation {
883                Ok(result) => break Ok(result),
884                Err(error) => {
885                    attempts += 1;
886                    last_error = Some(error);
887
888                    if attempts >= $max_retries {
889                        break Err(last_error.expect("Operation failed"));
890                    }
891
892                    // Simple linear backoff
893                    std::thread::sleep(std::time::Duration::from_millis(attempts * 100));
894                }
895            }
896        }
897    }};
898}