scirs2_stats/
unified_error_handling.rs

1//! Unified Error Handling System
2//!
3//! This module provides a comprehensive, unified interface to all error handling
4//! capabilities in the scirs2-stats library, combining diagnostics, monitoring,
5//! standardization, and recovery suggestions into a single cohesive system.
6
7use crate::error::{StatsError, StatsResult};
8use crate::error_diagnostics::{
9    generate_global_health_report, get_global_statistics, global_monitor, record_global_error,
10    ErrorMonitor, HealthReport,
11};
12use crate::error_handling_v2::{EnhancedError, ErrorBuilder, ErrorCode};
13use crate::error_standardization::{ErrorMessages, StandardizedErrorReporter};
14use scirs2_core::validation::{check_finite, check_positive};
15use std::sync::Once;
16use std::time::Instant;
17
18/// Unified error handling facade providing comprehensive error management
19pub struct UnifiedErrorHandler {
20    #[allow(dead_code)]
21    monitor: &'static ErrorMonitor,
22    start_time: Instant,
23}
24
25impl UnifiedErrorHandler {
26    /// Create a new unified error handler
27    pub fn new() -> Self {
28        Self {
29            monitor: global_monitor(),
30            start_time: Instant::now(),
31        }
32    }
33
34    /// Create and record a comprehensive error with automatic diagnostics
35    pub fn create_error(
36        &self,
37        code: ErrorCode,
38        operation: impl Into<String>,
39        message: impl Into<String>,
40    ) -> EnhancedError {
41        let operation_str = operation.into();
42
43        // Record the error for monitoring
44        record_global_error(code, &operation_str);
45
46        // Create enhanced error with context and suggestions
47        let base_error = StatsError::computation(message);
48
49        ErrorBuilder::new(code, operation_str).build(base_error)
50    }
51
52    /// Create error with parameter validation and automatic suggestions
53    pub fn create_validation_error(
54        &self,
55        code: ErrorCode,
56        operation: impl Into<String>,
57        parameter_name: &str,
58        parameter_value: impl std::fmt::Display,
59        validation_message: impl Into<String>,
60    ) -> EnhancedError {
61        let operation_str = operation.into();
62
63        // Record the error for monitoring
64        record_global_error(code, &operation_str);
65
66        // Create enhanced error with parameter context
67        let base_error = StatsError::invalid_argument(validation_message);
68
69        ErrorBuilder::new(code, operation_str)
70            .parameter(parameter_name, parameter_value)
71            .build(base_error)
72    }
73
74    /// Validate array and create appropriate error if validation fails
75    pub fn validate_array_or_error<T>(
76        &self,
77        data: &[T],
78        name: &str,
79        operation: &str,
80    ) -> StatsResult<()>
81    where
82        T: PartialOrd + Copy,
83    {
84        // Check if slice is empty
85        if data.is_empty() {
86            let code = ErrorCode::E2004;
87            record_global_error(code, operation);
88            return Err(ErrorMessages::empty_array(name));
89        }
90        Ok(())
91    }
92
93    /// Validate finite array and create appropriate error if validation fails
94    pub fn validate_finite_array_or_error(
95        &self,
96        data: &[f64],
97        name: &str,
98        operation: &str,
99    ) -> StatsResult<()> {
100        // Check if slice is empty
101        if data.is_empty() {
102            let code = ErrorCode::E2004;
103            record_global_error(code, operation);
104            return Err(ErrorMessages::empty_array(name));
105        }
106
107        // Check for finite values using core validation
108        for &value in data {
109            if check_finite(value, name).is_err() {
110                let code = if value.is_nan() {
111                    ErrorCode::E3005
112                } else if value.is_infinite() {
113                    ErrorCode::E3006
114                } else {
115                    ErrorCode::E1001
116                };
117                record_global_error(code, operation);
118                return Err(ErrorMessages::nan_detected(operation));
119            }
120        }
121        Ok(())
122    }
123
124    /// Validate probability and create appropriate error if validation fails
125    pub fn validate_probability_or_error(
126        &self,
127        value: f64,
128        name: &str,
129        operation: &str,
130    ) -> StatsResult<()> {
131        // Use scirs2-core validation
132        if scirs2_core::validation::check_probability(value, name).is_err() {
133            let code = if value.is_nan() {
134                ErrorCode::E3005
135            } else if !(0.0..=1.0).contains(&value) {
136                ErrorCode::E1003
137            } else {
138                ErrorCode::E1001
139            };
140
141            record_global_error(code, operation);
142            return Err(ErrorMessages::invalid_probability(name, value));
143        }
144        Ok(())
145    }
146
147    /// Validate positive value and create appropriate error if validation fails
148    pub fn validate_positive_or_error(
149        &self,
150        value: f64,
151        name: &str,
152        operation: &str,
153    ) -> StatsResult<()> {
154        // Use scirs2-core validation
155        if check_positive(value, name).is_err() {
156            let code = if value.is_nan() {
157                ErrorCode::E3005
158            } else if value.is_infinite() {
159                ErrorCode::E3006
160            } else if value <= 0.0 {
161                ErrorCode::E1002
162            } else {
163                ErrorCode::E1001
164            };
165
166            record_global_error(code, operation);
167            return Err(ErrorMessages::non_positive_value(name, value));
168        }
169        Ok(())
170    }
171
172    /// Generate a comprehensive error report with diagnostics
173    pub fn generate_comprehensive_report(
174        &self,
175        error: &StatsError,
176        context: Option<&str>,
177    ) -> String {
178        let mut report = StandardizedErrorReporter::generate_report(error, context);
179
180        // Add system health information
181        let health_report = generate_global_health_report();
182        if health_report.health_score < 80 {
183            report.push_str("\n🚨 SYSTEM HEALTH ALERT:\n");
184            report.push_str(&format!(
185                "Overall health score: {}/100\n",
186                health_report.health_score
187            ));
188
189            if !health_report.critical_issues.is_empty() {
190                report.push_str("Critical issues detected:\n");
191                for issue in &health_report.critical_issues {
192                    report.push_str(&format!(
193                        "  • {} (Severity: {})\n",
194                        issue.title, issue.severity
195                    ));
196                }
197            }
198        }
199
200        // Add monitoring statistics
201        let stats = get_global_statistics();
202        if stats.total_errors > 10 {
203            report.push_str(&format!(
204                "\n📊 Error Statistics: {} total errors, {:.4} errors/sec\n",
205                stats.total_errors, stats.error_rate
206            ));
207        }
208
209        report
210    }
211
212    /// Get current system health status
213    pub fn get_health_status(&self) -> HealthReport {
214        generate_global_health_report()
215    }
216
217    /// Check if system requires immediate attention
218    pub fn requires_immediate_attention(&self) -> bool {
219        let health_report = generate_global_health_report();
220        health_report.requires_immediate_action()
221    }
222
223    /// Get uptime since unified error handler creation
224    pub fn uptime(&self) -> std::time::Duration {
225        self.start_time.elapsed()
226    }
227
228    /// Print health summary to console
229    pub fn print_health_summary(&self) {
230        let health_report = generate_global_health_report();
231        println!("{}", health_report.to_formatted_string());
232    }
233}
234
235impl Default for UnifiedErrorHandler {
236    fn default() -> Self {
237        Self::new()
238    }
239}
240
241/// Global unified error handler instance
242static GLOBAL_HANDLER: Once = Once::new();
243static mut GLOBAL_HANDLER_INSTANCE: Option<UnifiedErrorHandler> = None;
244
245/// Get the global unified error handler instance
246#[allow(dead_code)]
247#[allow(static_mut_refs)]
248pub fn global_error_handler() -> &'static UnifiedErrorHandler {
249    unsafe {
250        GLOBAL_HANDLER.call_once(|| {
251            GLOBAL_HANDLER_INSTANCE = Some(UnifiedErrorHandler::new());
252        });
253        GLOBAL_HANDLER_INSTANCE.as_ref().unwrap()
254    }
255}
256
257/// Convenience macro for creating standardized errors with automatic monitoring
258#[macro_export]
259macro_rules! stats_error_unified {
260    ($code:expr, $op:expr, $msg:expr) => {
261        $crate::unified_error_handling::global_error_handler().create_error($code, $op, $msg)
262    };
263
264    ($code:expr, $op:expr, $msg:expr, $param:expr => $value:expr) => {
265        $crate::unified_error_handling::global_error_handler()
266            .create_validation_error($code, $op, $param, $value, $msg)
267    };
268}
269
270/// Convenience macro for validation with automatic error creation
271#[macro_export]
272macro_rules! validate_or_error {
273    (array: $data:expr, $name:expr, $op:expr) => {
274        $crate::unified_error_handling::global_error_handler()
275            .validate_array_or_error($data, $name, $op)?
276    };
277
278    (finite: $data:expr, $name:expr, $op:expr) => {
279        $crate::unified_error_handling::global_error_handler()
280            .validate_finite_array_or_error($data, $name, $op)?
281    };
282
283    (probability: $value:expr, $name:expr, $op:expr) => {
284        $crate::unified_error_handling::global_error_handler()
285            .validate_probability_or_error($value, $name, $op)?
286    };
287
288    (positive: $value:expr, $name:expr, $op:expr) => {
289        $crate::unified_error_handling::global_error_handler()
290            .validate_positive_or_error($value, $name, $op)?
291    };
292}
293
294/// Helper function to create standardized error messages
295#[allow(dead_code)]
296pub fn create_standardized_error(
297    error_type: &str,
298    parameter: &str,
299    value: &str,
300    operation: &str,
301) -> StatsError {
302    match error_type {
303        "dimension_mismatch" => ErrorMessages::dimension_mismatch(parameter, value),
304        "empty_array" => ErrorMessages::empty_array(parameter),
305        "non_positive" => {
306            ErrorMessages::non_positive_value(parameter, value.parse().unwrap_or(0.0))
307        }
308        "invalid_probability" => {
309            ErrorMessages::invalid_probability(parameter, value.parse().unwrap_or(-1.0))
310        }
311        "nan_detected" => ErrorMessages::nan_detected(operation),
312        "infinite_detected" => ErrorMessages::infinite_value_detected(operation),
313        "convergence_failure" => {
314            ErrorMessages::convergence_failure(operation, value.parse().unwrap_or(100))
315        }
316        _ => StatsError::invalid_argument(format!("Unknown error type: {}", error_type)),
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323    use crate::error_handling_v2::ErrorCode;
324
325    #[test]
326    fn test_unified_error_handler() {
327        let handler = UnifiedErrorHandler::new();
328
329        // Test error creation
330        let error = handler.create_error(ErrorCode::E3005, "test_operation", "Test error message");
331
332        assert_eq!(error.code, ErrorCode::E3005);
333        assert_eq!(error.context.operation, "test_operation");
334    }
335
336    #[test]
337    fn test_validation_errors() {
338        let handler = UnifiedErrorHandler::new();
339
340        // Test empty array validation
341        let empty_data: &[f64] = &[];
342        let result = handler.validate_array_or_error(empty_data, "test_array", "test_op");
343        assert!(result.is_err());
344
345        // Test NaN validation
346        let nandata = &[1.0, f64::NAN, 3.0];
347        let result = handler.validate_finite_array_or_error(nandata, "test_array", "test_op");
348        assert!(result.is_err());
349
350        // Test invalid probability
351        let result = handler.validate_probability_or_error(-0.5, "probability", "test_op");
352        assert!(result.is_err());
353
354        // Test non-positive value
355        let result = handler.validate_positive_or_error(-1.0, "positive_param", "test_op");
356        assert!(result.is_err());
357    }
358
359    #[test]
360    fn test_global_handler() {
361        let handler1 = global_error_handler();
362        let handler2 = global_error_handler();
363
364        // Should be the same instance
365        assert_eq!(handler1 as *const _, handler2 as *const _);
366    }
367
368    #[test]
369    fn test_macros() {
370        // Test error creation macro
371        let _error = stats_error_unified!(ErrorCode::E1001, "test_operation", "Test message");
372
373        // Test validation macro would need valid arrays to test properly
374        let validdata = &[1.0, 2.0, 3.0];
375        let result: Result<(), StatsError> = (|| {
376            validate_or_error!(finite: validdata, "testdata", "test_op");
377            Ok(())
378        })();
379        assert!(result.is_ok());
380    }
381}