Skip to main content

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// ErrorKind enum and additional From implementations
121pub mod error_kind;
122pub use error_kind::ErrorKind;
123
124// Recovery strategies and mechanisms
125pub mod recovery;
126pub use recovery::{
127    hints, ErrorAggregator, ErrorSeverity, RecoverableError, RecoveryHint, RecoveryStrategy,
128};
129
130// Async error handling
131#[cfg(feature = "async")]
132pub mod async_handling;
133#[cfg(feature = "async")]
134pub use async_handling::{
135    execute_witherror_aggregation, retry_with_exponential_backoff, with_timeout,
136    AsyncCircuitBreaker, AsyncErrorAggregator, AsyncProgressTracker, AsyncRetryExecutor,
137    TimeoutWrapper, TrackedAsyncOperation,
138};
139
140// Advanced error diagnostics
141pub mod diagnostics;
142pub use diagnostics::{
143    error, error_with_context, EnvironmentInfo, ErrorDiagnosticReport, ErrorDiagnostics,
144    ErrorOccurrence, ErrorPattern, PerformanceImpact,
145};
146
147// Circuit breaker and error recovery for production systems
148pub mod circuitbreaker;
149pub use circuitbreaker::{
150    get_circuitbreaker, list_circuitbreakers, CircuitBreaker, CircuitBreakerConfig,
151    CircuitBreakerStatus, CircuitState, FallbackStrategy, ResilientExecutor, RetryExecutor,
152    RetryPolicy,
153};
154
155// Convenience re-exports for common patterns
156pub use error::{
157    chainerror, check_dimensions, check_domain, check_value, converterror, validate, CoreError,
158    CoreResult, ErrorContext, ErrorLocation,
159};
160
161/// Beta 2 Enhanced Diagnostic Functions
162///
163/// Analyze an error with comprehensive diagnostics including Beta 2 features
164///
165/// # Errors
166///
167/// This function does not return errors but analyzes the provided error.
168#[must_use]
169#[allow(dead_code)]
170pub fn diagnoseerror(error: &CoreError) -> ErrorDiagnosticReport {
171    diagnoseerror_advanced(error, None, None)
172}
173
174/// Advanced error diagnosis function.
175///
176/// # Errors
177///
178/// This function does not return errors but analyzes the provided error.
179#[must_use]
180#[allow(dead_code)]
181pub fn diagnoseerror_advanced(
182    error: &CoreError,
183    context: Option<&str>,
184    domain: Option<&str>,
185) -> ErrorDiagnosticReport {
186    let diagnostics = ErrorDiagnostics::global();
187    let mut report = diagnostics.analyzeerror(error);
188
189    // Add predictive analysis if context is provided
190    if let Some(ctx) = context {
191        report.predictions = diagnostics.predict_potentialerrors(ctx);
192    }
193
194    // Add domain-specific recovery strategies if domain is provided
195    if let Some(dom) = domain {
196        report.domain_strategies = diagnostics.suggest_domain_recovery(error, dom);
197    }
198
199    report
200}
201
202/// Record an error for pattern analysis
203#[allow(dead_code)]
204pub fn recorderror(error: &CoreError, context: String) {
205    let diagnostics = ErrorDiagnostics::global();
206    diagnostics.recorderror(error, context);
207}
208
209/// Get predictive error analysis for a given context
210#[must_use]
211#[allow(dead_code)]
212pub fn context(context: &str) -> Vec<String> {
213    let diagnostics = ErrorDiagnostics::global();
214    diagnostics.predict_potentialerrors(context)
215}
216
217/// Get domain-specific recovery strategies for an error
218#[must_use]
219#[allow(dead_code)]
220pub fn get_recovery_strategies(error: &CoreError, domain: &str) -> Vec<String> {
221    let diagnostics = ErrorDiagnostics::global();
222    diagnostics.suggest_domain_recovery(error, domain)
223}
224
225pub mod prelude {
226    //! Prelude module for convenient imports
227    //! Commonly used error handling types and functions
228
229    pub use super::{
230        error, CircuitBreaker, CoreError, CoreResult, EnvironmentInfo, ErrorAggregator,
231        ErrorContext, ErrorDiagnosticReport, ErrorLocation, ErrorSeverity, RecoverableError,
232        RecoveryStrategy, RetryExecutor,
233    };
234
235    #[cfg(feature = "async")]
236    pub use super::{
237        retry_with_exponential_backoff, with_timeout, AsyncCircuitBreaker, AsyncProgressTracker,
238        AsyncRetryExecutor,
239    };
240
241    // Convenience macros
242    pub use crate::{computationerror, dimensionerror, domainerror, error_context, valueerror};
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248    use crate::error_context;
249    use std::time::Duration;
250
251    #[test]
252    fn testerror_integration() {
253        let error = CoreError::DomainError(error_context!("Test error"));
254        let recoverable = RecoverableError::error(error);
255
256        assert!(!recoverable.retryable);
257        assert_eq!(recoverable.severity, ErrorSeverity::Error);
258        assert!(!recoverable.hints.is_empty());
259    }
260
261    #[test]
262    fn test_retry_executor() {
263        let policy = RetryPolicy {
264            max_retries: 2,
265            initial_delay: Duration::from_millis(1),
266            ..Default::default()
267        };
268        let executor = RetryExecutor::new(policy);
269
270        let attempts = std::cell::RefCell::new(0);
271        let result = executor.execute(|| {
272            let mut count = attempts.borrow_mut();
273            *count += 1;
274            if *count == 1 {
275                Err(CoreError::ComputationError(error_context!(
276                    "Temporary failure"
277                )))
278            } else {
279                Ok(42)
280            }
281        });
282
283        assert_eq!(result.expect("Operation failed"), 42);
284        assert_eq!(*attempts.borrow(), 2);
285    }
286
287    #[test]
288    fn testerror_diagnostics() {
289        let error = CoreError::MemoryError(error_context!("Out of memory"));
290        let report = diagnoseerror(&error);
291
292        assert!(matches!(report.error, CoreError::MemoryError(_)));
293        assert_eq!(report.performance_impact, PerformanceImpact::High);
294        assert!(!report.contextual_suggestions.is_empty());
295    }
296
297    #[test]
298    fn test_circuitbreaker() {
299        use crate::error::circuitbreaker::{CircuitBreakerConfig, CircuitState};
300        use std::time::Duration;
301
302        // Configure circuit breaker with low threshold for testing
303        let config = CircuitBreakerConfig {
304            failure_threshold: 1,
305            success_threshold: 1,
306            timeout: Duration::from_secs(30),
307        };
308        let breaker = CircuitBreaker::new(config);
309
310        // First failure should work and trigger circuit opening
311        let result: std::result::Result<(), CoreError> =
312            breaker.execute(|| Err(CoreError::ComputationError(error_context!("Test failure"))));
313        assert!(result.is_err());
314
315        // Second call should be blocked by circuit breaker
316        let result = breaker.execute(|| Ok(42));
317        assert!(result.is_err());
318
319        let status = breaker.status();
320        assert_eq!(status.failure_count, 1);
321        assert_eq!(status.state, CircuitState::Open);
322    }
323
324    #[test]
325    fn testerror_aggregator() {
326        let mut aggregator = ErrorAggregator::new();
327
328        aggregator.add_simpleerror(CoreError::ValueError(error_context!("Error 1")));
329        aggregator.add_simpleerror(CoreError::DomainError(error_context!("Error 2")));
330
331        assert_eq!(aggregator.error_count(), 2);
332        assert!(aggregator.haserrors());
333
334        let summary = aggregator.summary();
335        assert!(summary.contains("Collected 2 error(s)"));
336    }
337
338    #[cfg(feature = "async")]
339    #[tokio::test]
340    async fn test_asyncerror_handling() {
341        use super::async_handling::*;
342
343        // Test timeout
344        let result = with_timeout(
345            async {
346                tokio::time::sleep(Duration::from_millis(100)).await;
347                Ok::<i32, CoreError>(42)
348            },
349            Duration::from_millis(50),
350        )
351        .await;
352
353        assert!(result.is_err());
354        assert!(matches!(result.unwrap_err(), CoreError::TimeoutError(_)));
355
356        // Test async retry
357        let executor = AsyncRetryExecutor::new(RecoveryStrategy::LinearBackoff {
358            max_attempts: 2,
359            delay: Duration::from_millis(1),
360        });
361
362        let mut attempts = 0;
363        let result = executor
364            .execute(|| {
365                attempts += 1;
366                async move {
367                    if attempts == 1 {
368                        Err(CoreError::ComputationError(error_context!("Async failure")))
369                    } else {
370                        Ok(42)
371                    }
372                }
373            })
374            .await;
375
376        assert_eq!(result.expect("Operation failed"), 42);
377        assert_eq!(attempts, 2);
378    }
379}