scirs2_core/error/
mod.rs

1//! Advanced error handling and recovery system for ``SciRS2``
2//!
3//! This module provides a comprehensive error handling framework including:
4//! - Core error types and context management
5//! - Advanced recovery strategies with retry mechanisms
6//! - Circuit breaker patterns for fault tolerance
7//! - Async error handling with timeout and progress tracking
8//! - Comprehensive error diagnostics and pattern analysis
9//! - Environment-aware error analysis and suggestions
10//!
11//! ## Quick Start
12//!
13//! ```rust
14//! use scirs2_core::error::{CoreError, CoreResult, RecoverableError};
15//! use scirs2_core::error_context;
16//!
17//! fn example_operation() -> CoreResult<i32> {
18//!     // Basic error with context
19//!     if false {
20//!         return Err(CoreError::ComputationError(
21//!             error_context!("Operation failed")
22//!         ));
23//!     }
24//!     
25//!     Ok(42)
26//! }
27//!
28//! // Enhanced error with recovery suggestions
29//! let error = CoreError::MemoryError(error_context!("Out of memory"));
30//! let recoverable = RecoverableError::error(error);
31//! println!("{}", recoverable.recovery_report());
32//! ```
33//!
34//! ## Advanced Features
35//!
36//! ### Retry Mechanisms
37//!
38//! ```rust
39//! use scirs2_core::error::recovery::{RetryExecutor, RecoveryStrategy};
40//! use std::time::Duration;
41//!
42//! let executor = RetryExecutor::new(RecoveryStrategy::ExponentialBackoff {
43//!     max_attempts: 3,
44//!     initialdelay: Duration::from_millis(100),
45//!     maxdelay: Duration::from_secs(5),
46//!     multiplier: 2.0,
47//! });
48//!
49//! let result = executor.execute(|| {
50//!     // Your operation here
51//!     Ok(42)
52//! });
53//! ```
54//!
55//! ### Circuit Breaker Pattern
56//!
57//! ```rust
58//! use scirs2_core::error::recovery::CircuitBreaker;
59//! use std::time::Duration;
60//!
61//! let circuitbreaker = CircuitBreaker::new(
62//!     5, // failure threshold
63//!     Duration::from_secs(30), // timeout
64//!     Duration::from_secs(60), // recovery timeout
65//! );
66//!
67//! let result = circuitbreaker.execute(|| {
68//!     // Your operation here
69//!     Ok(42)
70//! });
71//! ```
72//!
73//! ### Async Error Handling
74//!
75//! ```rust
76//! use scirs2_core::error::recovery::{RecoveryStrategy, RetryExecutor};
77//! use std::time::Duration;
78//!
79//! // Synchronous retry example
80//! let executor = RetryExecutor::new(
81//!     RecoveryStrategy::LinearBackoff {
82//!         max_attempts: 3,
83//!         delay: Duration::from_millis(100),
84//!     }
85//! );
86//!
87//! let mut counter = 0;
88//! let retry_result = executor.execute(|| {
89//!     counter += 1;
90//!     if counter < 3 {
91//!         Err(scirs2_core::error::CoreError::ComputationError(
92//!             scirs2_core::error::ErrorContext::new("Temporary failure".to_string())
93//!         ))
94//!     } else {
95//!         Ok::<i32, scirs2_core::error::CoreError>(123)
96//!     }
97//! });
98//! assert!(retry_result.is_ok());
99//! ```
100//!
101//! ### Error Diagnostics
102//!
103//! ```rust
104//! use scirs2_core::error::diagnostics::error;
105//! use scirs2_core::error::{CoreError, ErrorContext, ErrorLocation};
106//!
107//! let err = CoreError::ConvergenceError(
108//!     ErrorContext::new("Failed to converge")
109//!         .with_location(ErrorLocation::new(file!(), line!()))
110//! );
111//! let diagnostics = error(&err);
112//!
113//! println!("{}", diagnostics); // Comprehensive diagnostic report
114//! ```
115
116// Core error types and functionality
117#[allow(clippy::module_inception)]
118mod error;
119
120// Recovery strategies and mechanisms
121pub mod recovery;
122pub use recovery::{
123    hints, ErrorAggregator, ErrorSeverity, RecoverableError, RecoveryHint, RecoveryStrategy,
124};
125
126// Async error handling
127#[cfg(feature = "async")]
128pub mod async_handling;
129#[cfg(feature = "async")]
130pub use async_handling::{
131    execute_witherror_aggregation, retry_with_exponential_backoff, with_timeout,
132    AsyncCircuitBreaker, AsyncErrorAggregator, AsyncProgressTracker, AsyncRetryExecutor,
133    TimeoutWrapper, TrackedAsyncOperation,
134};
135
136// Advanced error diagnostics
137pub mod diagnostics;
138pub use diagnostics::{
139    error, error_with_context, EnvironmentInfo, ErrorDiagnosticReport, ErrorDiagnostics,
140    ErrorOccurrence, ErrorPattern, PerformanceImpact,
141};
142
143// Circuit breaker and error recovery for production systems
144pub mod circuitbreaker;
145pub use circuitbreaker::{
146    get_circuitbreaker, list_circuitbreakers, CircuitBreaker, CircuitBreakerConfig,
147    CircuitBreakerStatus, CircuitState, FallbackStrategy, ResilientExecutor, RetryExecutor,
148    RetryPolicy,
149};
150
151// Convenience re-exports for common patterns
152pub use error::{
153    chainerror, check_dimensions, check_domain, check_value, converterror, validate, CoreError,
154    CoreResult, ErrorContext, ErrorLocation,
155};
156
157/// Beta 2 Enhanced Diagnostic Functions
158///
159/// Analyze an error with comprehensive diagnostics including Beta 2 features
160///
161/// # Errors
162///
163/// This function does not return errors but analyzes the provided error.
164#[must_use]
165#[allow(dead_code)]
166pub fn diagnoseerror(error: &CoreError) -> ErrorDiagnosticReport {
167    diagnoseerror_advanced(error, None, None)
168}
169
170/// Advanced error diagnosis function.
171///
172/// # Errors
173///
174/// This function does not return errors but analyzes the provided error.
175#[must_use]
176#[allow(dead_code)]
177pub fn diagnoseerror_advanced(
178    error: &CoreError,
179    context: Option<&str>,
180    domain: Option<&str>,
181) -> ErrorDiagnosticReport {
182    let diagnostics = ErrorDiagnostics::global();
183    let mut report = diagnostics.analyzeerror(error);
184
185    // Add predictive analysis if context is provided
186    if let Some(ctx) = context {
187        report.predictions = diagnostics.predict_potentialerrors(ctx);
188    }
189
190    // Add domain-specific recovery strategies if domain is provided
191    if let Some(dom) = domain {
192        report.domain_strategies = diagnostics.suggest_domain_recovery(error, dom);
193    }
194
195    report
196}
197
198/// Record an error for pattern analysis
199#[allow(dead_code)]
200pub fn recorderror(error: &CoreError, context: String) {
201    let diagnostics = ErrorDiagnostics::global();
202    diagnostics.recorderror(error, context);
203}
204
205/// Get predictive error analysis for a given context
206#[must_use]
207#[allow(dead_code)]
208pub fn context(context: &str) -> Vec<String> {
209    let diagnostics = ErrorDiagnostics::global();
210    diagnostics.predict_potentialerrors(context)
211}
212
213/// Get domain-specific recovery strategies for an error
214#[must_use]
215#[allow(dead_code)]
216pub fn get_recovery_strategies(error: &CoreError, domain: &str) -> Vec<String> {
217    let diagnostics = ErrorDiagnostics::global();
218    diagnostics.suggest_domain_recovery(error, domain)
219}
220
221pub mod prelude {
222    //! Prelude module for convenient imports
223    //! Commonly used error handling types and functions
224
225    pub use super::{
226        error, CircuitBreaker, CoreError, CoreResult, EnvironmentInfo, ErrorAggregator,
227        ErrorContext, ErrorDiagnosticReport, ErrorLocation, ErrorSeverity, RecoverableError,
228        RecoveryStrategy, RetryExecutor,
229    };
230
231    #[cfg(feature = "async")]
232    pub use super::{
233        retry_with_exponential_backoff, with_timeout, AsyncCircuitBreaker, AsyncProgressTracker,
234        AsyncRetryExecutor,
235    };
236
237    // Convenience macros
238    pub use crate::{computationerror, dimensionerror, domainerror, error_context, valueerror};
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    use crate::error_context;
245    use std::time::Duration;
246
247    #[test]
248    fn testerror_integration() {
249        let error = CoreError::DomainError(error_context!("Test error"));
250        let recoverable = RecoverableError::error(error);
251
252        assert!(!recoverable.retryable);
253        assert_eq!(recoverable.severity, ErrorSeverity::Error);
254        assert!(!recoverable.hints.is_empty());
255    }
256
257    #[test]
258    fn test_retry_executor() {
259        let policy = RetryPolicy {
260            max_retries: 2,
261            initial_delay: Duration::from_millis(1),
262            ..Default::default()
263        };
264        let executor = RetryExecutor::new(policy);
265
266        let attempts = std::cell::RefCell::new(0);
267        let result = executor.execute(|| {
268            let mut count = attempts.borrow_mut();
269            *count += 1;
270            if *count == 1 {
271                Err(CoreError::ComputationError(error_context!(
272                    "Temporary failure"
273                )))
274            } else {
275                Ok(42)
276            }
277        });
278
279        assert_eq!(result.unwrap(), 42);
280        assert_eq!(*attempts.borrow(), 2);
281    }
282
283    #[test]
284    fn testerror_diagnostics() {
285        let error = CoreError::MemoryError(error_context!("Out of memory"));
286        let report = diagnoseerror(&error);
287
288        assert!(matches!(report.error, CoreError::MemoryError(_)));
289        assert_eq!(report.performance_impact, PerformanceImpact::High);
290        assert!(!report.contextual_suggestions.is_empty());
291    }
292
293    #[test]
294    fn test_circuitbreaker() {
295        use crate::error::circuitbreaker::{CircuitBreakerConfig, CircuitState};
296        use std::time::Duration;
297
298        // Configure circuit breaker with low threshold for testing
299        let config = CircuitBreakerConfig {
300            failure_threshold: 1,
301            success_threshold: 1,
302            timeout: Duration::from_secs(30),
303        };
304        let breaker = CircuitBreaker::new(config);
305
306        // First failure should work and trigger circuit opening
307        let result: std::result::Result<(), CoreError> =
308            breaker.execute(|| Err(CoreError::ComputationError(error_context!("Test failure"))));
309        assert!(result.is_err());
310
311        // Second call should be blocked by circuit breaker
312        let result = breaker.execute(|| Ok(42));
313        assert!(result.is_err());
314
315        let status = breaker.status();
316        assert_eq!(status.failure_count, 1);
317        assert_eq!(status.state, CircuitState::Open);
318    }
319
320    #[test]
321    fn testerror_aggregator() {
322        let mut aggregator = ErrorAggregator::new();
323
324        aggregator.add_simpleerror(CoreError::ValueError(error_context!("Error 1")));
325        aggregator.add_simpleerror(CoreError::DomainError(error_context!("Error 2")));
326
327        assert_eq!(aggregator.error_count(), 2);
328        assert!(aggregator.haserrors());
329
330        let summary = aggregator.summary();
331        assert!(summary.contains("Collected 2 error(s)"));
332    }
333
334    #[cfg(feature = "async")]
335    #[tokio::test]
336    async fn test_asyncerror_handling() {
337        use super::async_handling::*;
338
339        // Test timeout
340        let result = with_timeout(
341            async {
342                tokio::time::sleep(Duration::from_millis(100)).await;
343                Ok::<i32, CoreError>(42)
344            },
345            Duration::from_millis(50),
346        )
347        .await;
348
349        assert!(result.is_err());
350        assert!(matches!(result.unwrap_err(), CoreError::TimeoutError(_)));
351
352        // Test async retry
353        let executor = AsyncRetryExecutor::new(RecoveryStrategy::LinearBackoff {
354            max_attempts: 2,
355            delay: Duration::from_millis(1),
356        });
357
358        let mut attempts = 0;
359        let result = executor
360            .execute(|| {
361                attempts += 1;
362                async move {
363                    if attempts == 1 {
364                        Err(CoreError::ComputationError(error_context!("Async failure")))
365                    } else {
366                        Ok(42)
367                    }
368                }
369            })
370            .await;
371
372        assert_eq!(result.unwrap(), 42);
373        assert_eq!(attempts, 2);
374    }
375}