1use std::collections::HashMap;
11use std::fmt;
12use std::sync::{Arc, Mutex, OnceLock};
13use std::time::{Duration, SystemTime};
14
15use crate::error::CoreError;
16
17#[derive(Debug, Clone)]
19pub struct EnvironmentInfo {
20 pub os: String,
22 pub arch: String,
24 pub available_memory: Option<u64>,
26 pub cpu_cores: Option<usize>,
28 pub rustc_version: Option<String>,
30 pub scirs2_version: String,
32 pub features: Vec<String>,
34 pub env_vars: HashMap<String, String>,
36}
37
38impl Default for EnvironmentInfo {
39 fn default() -> Self {
40 let mut env_vars = HashMap::new();
41
42 let relevant_vars = [
44 "RUST_LOG",
45 "RUST_BACKTRACE",
46 "OMP_NUM_THREADS",
47 "MKL_NUM_THREADS",
48 "OPENBLAS_NUM_THREADS",
49 "RAYON_NUM_THREADS",
50 "CARGO_MANIFEST_DIR",
51 ];
52
53 for var in &relevant_vars {
54 if let Ok(value) = std::env::var(var) {
55 env_vars.insert(var.to_string(), value);
56 }
57 }
58
59 Self {
60 os: std::env::consts::OS.to_string(),
61 arch: std::env::consts::ARCH.to_string(),
62 available_memory: Self::get_available_memory(),
63 cpu_cores: std::thread::available_parallelism().ok().map(|n| n.get()),
64 rustc_version: option_env!("RUSTC_VERSION").map(|s| s.to_string()),
65 scirs2_version: env!("CARGO_PKG_VERSION").to_string(),
66 features: Self::get_enabled_features(),
67 env_vars,
68 }
69 }
70}
71
72impl EnvironmentInfo {
73 fn get_available_memory() -> Option<u64> {
75 #[cfg(unix)]
78 {
79 if let Ok(pages) = std::process::Command::new("getconf")
80 .args(["_PHYS_PAGES"])
81 .output()
82 {
83 if let Ok(pages_str) = String::from_utf8(pages.stdout) {
84 if let Ok(pages_num) = pages_str.trim().parse::<u64>() {
85 if let Ok(page_size) = std::process::Command::new("getconf")
86 .args(["PAGE_SIZE"])
87 .output()
88 {
89 if let Ok(sizestr) = String::from_utf8(page_size.stdout) {
90 if let Ok(size_num) = sizestr.trim().parse::<u64>() {
91 return Some(pages_num * size_num);
92 }
93 }
94 }
95 }
96 }
97 }
98 }
99 None
100 }
101
102 #[allow(clippy::vec_init_then_push)]
104 fn get_enabled_features() -> Vec<String> {
105 #[allow(unused_mut)]
106 let mut features = Vec::with_capacity(5);
107
108 #[cfg(feature = "parallel")]
109 features.push("parallel".to_string());
110
111 #[cfg(feature = "simd")]
112 features.push("simd".to_string());
113
114 #[cfg(feature = "gpu")]
115 features.push("gpu".to_string());
116
117 #[cfg(feature = "openblas")]
118 features.push("openblas".to_string());
119
120 #[cfg(feature = "profiling")]
123 features.push("profiling".to_string());
124
125 features
126 }
127}
128
129#[derive(Debug, Clone)]
131pub struct ErrorOccurrence {
132 pub errortype: String,
134 pub timestamp: SystemTime,
136 pub context: String,
138 pub location: Option<String>,
140 pub metadata: HashMap<String, String>,
142}
143
144impl ErrorOccurrence {
145 pub fn error(error: &CoreError, context: String) -> Self {
147 let errortype = format!("{error:?}")
148 .split('(')
149 .next()
150 .unwrap_or("Unknown")
151 .to_string();
152
153 Self {
154 errortype,
155 timestamp: SystemTime::now(),
156 context,
157 location: None,
158 metadata: HashMap::new(),
159 }
160 }
161
162 pub fn with_location<S: Into<String>>(mut self, location: S) -> Self {
164 self.location = Some(location.into());
165 self
166 }
167
168 pub fn with_metadata<K, V>(mut self, key: K, value: V) -> Self
170 where
171 K: Into<String>,
172 V: Into<String>,
173 {
174 self.metadata.insert(key.into(), value.into());
175 self
176 }
177}
178
179#[derive(Debug, Clone)]
181pub struct ErrorPattern {
182 pub description: String,
184 pub errortypes: Vec<String>,
186 pub frequency: usize,
188 pub common_contexts: Vec<String>,
190 pub suggestions: Vec<String>,
192}
193
194#[derive(Debug)]
196pub struct ErrorDiagnostics {
197 environment: EnvironmentInfo,
199 error_history: Arc<Mutex<Vec<ErrorOccurrence>>>,
201 max_history: usize,
203 patterns: Vec<ErrorPattern>,
205}
206
207static GLOBAL_DIAGNOSTICS: OnceLock<ErrorDiagnostics> = OnceLock::new();
208
209impl ErrorDiagnostics {
210 pub fn new() -> Self {
212 Self {
213 environment: EnvironmentInfo::default(),
214 error_history: Arc::new(Mutex::new(Vec::new())),
215 max_history: 1000,
216 patterns: Self::initialize_patterns(),
217 }
218 }
219
220 pub fn global() -> &'static ErrorDiagnostics {
222 GLOBAL_DIAGNOSTICS.get_or_init(Self::new)
223 }
224
225 pub fn recorderror(&self, error: &CoreError, context: String) {
227 let occurrence = ErrorOccurrence::error(error, context);
228
229 let mut history = self.error_history.lock().unwrap();
230 history.push(occurrence);
231
232 if history.len() > self.max_history {
234 history.remove(0);
235 }
236 }
237
238 pub fn analyzeerror(&self, error: &CoreError) -> ErrorDiagnosticReport {
240 let mut report = ErrorDiagnosticReport::error(error.clone());
241
242 report.environment = Some(self.environment.clone());
244
245 report.patterns = self.find_matching_patterns(error);
247
248 report.recent_occurrences = self.find_recent_similarerrors(error, Duration::from_secs(300)); report.performance_impact = self.assess_performance_impact(error);
253
254 report.contextual_suggestions = self.generate_contextual_suggestions(error, &report);
256
257 report.environment_diagnostics = self.diagnose_environment_issues(error);
259
260 report
261 }
262
263 fn find_matching_patterns(&self, error: &CoreError) -> Vec<ErrorPattern> {
265 let errortype = format!("{error:?}")
266 .split('(')
267 .next()
268 .unwrap_or("Unknown")
269 .to_string();
270
271 self.patterns
272 .iter()
273 .filter(|pattern| pattern.errortypes.contains(&errortype))
274 .cloned()
275 .collect()
276 }
277
278 fn find_recent_similarerrors(
280 &self,
281 error: &CoreError,
282 window: Duration,
283 ) -> Vec<ErrorOccurrence> {
284 let errortype = format!("{error:?}")
285 .split('(')
286 .next()
287 .unwrap_or("Unknown")
288 .to_string();
289 let cutoff = SystemTime::now() - window;
290
291 let history = self.error_history.lock().unwrap();
292 history
293 .iter()
294 .filter(|occurrence| {
295 occurrence.errortype == errortype && occurrence.timestamp >= cutoff
296 })
297 .cloned()
298 .collect()
299 }
300
301 fn assess_performance_impact(&self, error: &CoreError) -> PerformanceImpact {
303 match error {
304 CoreError::MemoryError(_) => PerformanceImpact::High,
305 CoreError::TimeoutError(_) => PerformanceImpact::High,
306 CoreError::ConvergenceError(_) => PerformanceImpact::Medium,
307 CoreError::ComputationError(_) => PerformanceImpact::Medium,
308 CoreError::DomainError(_) | CoreError::ValueError(_) => PerformanceImpact::Low,
309 _ => PerformanceImpact::Unknown,
310 }
311 }
312
313 fn generate_contextual_suggestions(
315 &self,
316 error: &CoreError,
317 report: &ErrorDiagnosticReport,
318 ) -> Vec<String> {
319 let mut suggestions = Vec::new();
320
321 if let Some(env) = &report.environment {
323 if env.available_memory.is_some_and(|mem| mem < 2_000_000_000) {
324 suggestions.push(
326 "Consider using memory-efficient algorithms for large datasets".to_string(),
327 );
328 }
329
330 if env.cpu_cores == Some(1) {
331 suggestions.push(
332 "Single-core system detected - parallel algorithms may not provide benefits"
333 .to_string(),
334 );
335 }
336
337 if !env.features.contains(&"simd".to_string()) {
338 suggestions.push(
339 "SIMD optimizations not enabled - consider enabling for better performance"
340 .to_string(),
341 );
342 }
343 }
344
345 for pattern in &report.patterns {
347 suggestions.extend(pattern.suggestions.clone());
348 }
349
350 if report.recent_occurrences.len() > 3 {
352 suggestions.push("This error has occurred frequently recently - consider reviewing input data or algorithm parameters".to_string());
353 }
354
355 suggestions
356 }
357
358 fn diagnose_environment_issues(&self, error: &CoreError) -> Vec<String> {
360 let mut diagnostics = Vec::new();
361
362 match error {
363 CoreError::MemoryError(_) => {
364 if let Some(mem) = self.environment.available_memory {
365 diagnostics.push(format!(
366 "Available memory: {:.2} GB",
367 mem as f64 / 1_000_000_000.0
368 ));
369
370 if mem < 4_000_000_000 {
372 diagnostics.push(
373 "Low memory detected - consider using memory-efficient algorithms"
374 .to_string(),
375 );
376 }
377 if mem > 64_000_000_000 {
378 diagnostics.push(
379 "High memory system - can use in-memory algorithms for large datasets"
380 .to_string(),
381 );
382 }
383 }
384
385 if let Some(threads) = self.environment.env_vars.get("OMP_NUM_THREADS") {
387 diagnostics.push(format!("OpenMP threads: {threads}"));
388 }
389
390 if !self
392 .environment
393 .features
394 .contains(&"memory_efficient".to_string())
395 {
396 diagnostics.push(
397 "Memory-efficient algorithms not enabled - consider enabling this feature"
398 .to_string(),
399 );
400 }
401 }
402
403 CoreError::ComputationError(_) => {
404 if let Some(cores) = self.environment.cpu_cores {
405 diagnostics.push(format!("CPU cores available: {cores}"));
406
407 if cores == 1 {
409 diagnostics.push(
410 "Single-core system - parallel algorithms won't help".to_string(),
411 );
412 } else if cores > 32 {
413 diagnostics.push(
414 "High-core count system - consider NUMA-aware algorithms".to_string(),
415 );
416 }
417 }
418
419 #[cfg(debug_assertions)]
421 diagnostics.push("Running in debug mode - performance may be reduced".to_string());
422
423 if !self.environment.features.contains(&"parallel".to_string()) {
425 diagnostics.push(
426 "Parallel processing not enabled - could improve performance".to_string(),
427 );
428 }
429 if !self.environment.features.contains(&"simd".to_string()) {
430 diagnostics.push(
431 "SIMD optimizations not enabled - could improve numerical performance"
432 .to_string(),
433 );
434 }
435 }
436
437 CoreError::TimeoutError(_) => {
438 diagnostics
439 .push("Timeout detected - operation took longer than expected".to_string());
440 if let Some(cores) = self.environment.cpu_cores {
441 if cores > 1 && !self.environment.features.contains(&"parallel".to_string()) {
442 diagnostics.push(
443 "Multi-core system but parallel processing not enabled".to_string(),
444 );
445 }
446 }
447 }
448
449 _ => {}
450 }
451
452 self.add_general_environment_diagnostics(&mut diagnostics);
454
455 diagnostics
456 }
457
458 fn add_general_environment_diagnostics(&self, diagnostics: &mut Vec<String>) {
460 if let (Some(cores), Some(omp_threads)) = (
462 self.environment.cpu_cores,
463 self.environment.env_vars.get("OMP_NUM_THREADS"),
464 ) {
465 if let Ok(omp_count) = omp_threads.parse::<usize>() {
466 if omp_count > cores {
467 diagnostics.push(
468 "OMP_NUM_THREADS exceeds available cores - may cause oversubscription"
469 .to_string(),
470 );
471 }
472 }
473 }
474
475 if self.environment.features.contains(&"openblas".to_string()) {
477 diagnostics.push("Using OpenBLAS backend - good for general computations".to_string());
478 } else if self.environment.features.contains(&"intel-mkl".to_string()) {
479 diagnostics.push("Using Intel MKL backend - optimized for Intel CPUs".to_string());
480 } else if self.environment.features.contains(&"linalg".to_string()) {
481 diagnostics.push(
482 "Linear algebra backend available but no optimized BLAS detected".to_string(),
483 );
484 }
485
486 if self.environment.features.contains(&"gpu".to_string()) {
488 diagnostics.push("GPU acceleration available".to_string());
489 if self.environment.features.contains(&"cuda".to_string()) {
490 diagnostics.push("CUDA backend enabled for NVIDIA GPUs".to_string());
491 }
492 }
493 }
494
495 #[allow(dead_code)]
497 pub fn predict_potentialerrors(&self, context: &str) -> Vec<String> {
498 let mut predictions = Vec::new();
499 let history = self.error_history.lock().unwrap();
500
501 let mut error_counts: HashMap<String, usize> = HashMap::new();
503 let recent_cutoff = SystemTime::now() - Duration::from_secs(3600); for occurrence in history.iter() {
506 if occurrence.timestamp >= recent_cutoff {
507 *error_counts
508 .entry(occurrence.errortype.clone())
509 .or_insert(0) += 1;
510 }
511 }
512
513 for (errortype, count) in error_counts {
515 if count >= 3 {
516 predictions.push(format!(
517 "High risk of {errortype} based on recent frequency ({count}x in last hour)"
518 ));
519 }
520 }
521
522 if (context.contains("matrix") || context.contains("linear_algebra"))
524 && self
525 .environment
526 .available_memory
527 .is_some_and(|mem| mem < 8_000_000_000)
528 {
529 predictions.push("Potential memory issues with large matrix operations".to_string());
530 }
531
532 if context.contains("optimization") || context.contains("solver") {
533 predictions.push(
534 "Potential convergence issues - consider robust initial conditions".to_string(),
535 );
536 }
537
538 if context.contains("parallel") && self.environment.cpu_cores == Some(1) {
539 predictions
540 .push("Parallel algorithms may not be effective on single-core system".to_string());
541 }
542
543 predictions
544 }
545
546 #[allow(dead_code)]
548 pub fn suggest_domain_recovery(&self, error: &CoreError, domain: &str) -> Vec<String> {
549 let mut strategies = Vec::new();
550
551 match domain {
552 "linear_algebra" => match error {
553 CoreError::MemoryError(_) => {
554 strategies.extend(vec![
555 "Use iterative solvers instead of direct factorization".to_string(),
556 "Implement block algorithms for large matrices".to_string(),
557 "Consider sparse matrix representations".to_string(),
558 "Use out-of-core matrix algorithms".to_string(),
559 ]);
560 }
561 CoreError::ConvergenceError(_) => {
562 strategies.extend(vec![
563 "Apply preconditioning (ILU, SSOR, or Jacobi)".to_string(),
564 "Use more robust factorization (SVD instead of LU)".to_string(),
565 "Check matrix conditioning and apply regularization".to_string(),
566 "Try different solver algorithms (GMRES, BiCGSTAB)".to_string(),
567 ]);
568 }
569 _ => {}
570 },
571
572 "optimization" => match error {
573 CoreError::ConvergenceError(_) => {
574 strategies.extend(vec![
575 "Use trust region methods for better global convergence".to_string(),
576 "Apply line search algorithms with backtracking".to_string(),
577 "Try multiple random starting points".to_string(),
578 "Use gradient-free methods for noisy objectives".to_string(),
579 "Implement adaptive step size control".to_string(),
580 ]);
581 }
582 CoreError::DomainError(_) => {
583 strategies.extend(vec![
584 "Add constraint handling and projection operators".to_string(),
585 "Use barrier methods for constrained optimization".to_string(),
586 "Implement bounds checking and clipping".to_string(),
587 ]);
588 }
589 _ => {}
590 },
591
592 "statistics" => match error {
593 CoreError::DomainError(_) => {
594 strategies.extend(vec![
595 "Use robust statistical methods for outliers".to_string(),
596 "Apply data transformation (log, Box-Cox)".to_string(),
597 "Implement missing data handling strategies".to_string(),
598 "Use non-parametric methods for non-normal data".to_string(),
599 ]);
600 }
601 CoreError::ComputationError(_) => {
602 strategies.extend(vec![
603 "Use numerically stable algorithms (Welford for variance)".to_string(),
604 "Apply importance sampling for rare events".to_string(),
605 "Use bootstrap methods for robust estimates".to_string(),
606 ]);
607 }
608 _ => {}
609 },
610
611 "signal_processing" => match error {
612 CoreError::MemoryError(_) => {
613 strategies.extend(vec![
614 "Use streaming FFT algorithms".to_string(),
615 "Implement overlap-add/overlap-save methods".to_string(),
616 "Use decimation for reduced sample rates".to_string(),
617 ]);
618 }
619 CoreError::ComputationError(_) => {
620 strategies.extend(vec![
621 "Use windowing to reduce spectral leakage".to_string(),
622 "Apply zero-padding for better frequency resolution".to_string(),
623 "Use advanced spectral estimation methods".to_string(),
624 ]);
625 }
626 _ => {}
627 },
628 _ => {
629 strategies.push("Consider using more robust numerical algorithms".to_string());
631 strategies.push("Implement error checking and data validation".to_string());
632 strategies.push("Use iterative refinement for better accuracy".to_string());
633 }
634 }
635
636 strategies
637 }
638
639 fn initialize_patterns() -> Vec<ErrorPattern> {
641 vec![
642 ErrorPattern {
643 description: "Memory allocation failures in large matrix operations".to_string(),
644 errortypes: vec!["MemoryError".to_string()],
645 frequency: 0,
646 common_contexts: vec![
647 "matrix_multiplication".to_string(),
648 "decomposition".to_string(),
649 ],
650 suggestions: vec![
651 "Use chunked processing for large matrices".to_string(),
652 "Consider using f32 instead of f64 to reduce memory usage".to_string(),
653 "Enable out-of-core algorithms if available".to_string(),
654 "Use memory-mapped arrays for very large datasets".to_string(),
655 "Consider distributed computing for extremely large problems".to_string(),
656 ],
657 },
658 ErrorPattern {
659 description: "Convergence failures in iterative algorithms".to_string(),
660 errortypes: vec!["ConvergenceError".to_string()],
661 frequency: 0,
662 common_contexts: vec!["optimization".to_string(), "linear_solver".to_string()],
663 suggestions: vec![
664 "Increase maximum iteration count".to_string(),
665 "Adjust convergence tolerance".to_string(),
666 "Try different initial conditions".to_string(),
667 "Use preconditioning to improve convergence".to_string(),
668 "Consider adaptive step sizes or trust region methods".to_string(),
669 "Check problem conditioning and scaling".to_string(),
670 ],
671 },
672 ErrorPattern {
673 description: "Shape mismatches in array operations".to_string(),
674 errortypes: vec!["ShapeError".to_string(), "DimensionError".to_string()],
675 frequency: 0,
676 common_contexts: vec!["matrix_operations".to_string(), "broadcasting".to_string()],
677 suggestions: vec![
678 "Check input array shapes before operations".to_string(),
679 "Use reshaping or broadcasting to make arrays compatible".to_string(),
680 "Verify matrix multiplication dimension compatibility (A: m×k, B: k×n)"
681 .to_string(),
682 "Consider using automatic broadcasting utilities".to_string(),
683 "Use array protocol for mixed array type compatibility".to_string(),
684 ],
685 },
686 ErrorPattern {
687 description: "Domain errors with mathematical functions".to_string(),
688 errortypes: vec!["DomainError".to_string()],
689 frequency: 0,
690 common_contexts: vec!["special_functions".to_string(), "statistics".to_string()],
691 suggestions: vec![
692 "Check input ranges for mathematical functions".to_string(),
693 "Handle edge cases (zero, negative values, infinities)".to_string(),
694 "Use input validation before calling functions".to_string(),
695 "Consider using robust numerical methods for ill-conditioned problems"
696 .to_string(),
697 "Use IEEE 754 special value handling where appropriate".to_string(),
698 ],
699 },
700 ErrorPattern {
702 description: "GPU memory exhaustion in accelerated computations".to_string(),
703 errortypes: vec!["MemoryError".to_string(), "ComputationError".to_string()],
704 frequency: 0,
705 common_contexts: vec![
706 "gpu_acceleration".to_string(),
707 "neural_networks".to_string(),
708 ],
709 suggestions: vec![
710 "Reduce batch size to fit GPU memory".to_string(),
711 "Use gradient accumulation for large batches".to_string(),
712 "Enable mixed precision training (fp16/fp32)".to_string(),
713 "Consider model parallelism for very large models".to_string(),
714 "Use CPU fallback for computations that don't fit on GPU".to_string(),
715 ],
716 },
717 ErrorPattern {
718 description: "Numerical instability in scientific computations".to_string(),
719 errortypes: vec![
720 "ComputationError".to_string(),
721 "ConvergenceError".to_string(),
722 ],
723 frequency: 0,
724 common_contexts: vec![
725 "linear_algebra".to_string(),
726 "ode_solving".to_string(),
727 "pde_solving".to_string(),
728 ],
729 suggestions: vec![
730 "Use higher precision (f64 instead of f32)".to_string(),
731 "Apply numerical stabilization techniques".to_string(),
732 "Check condition numbers and matrix rank".to_string(),
733 "Use pivoting strategies in decompositions".to_string(),
734 "Consider regularization for ill-posed problems".to_string(),
735 "Use iterative refinement for better accuracy".to_string(),
736 ],
737 },
738 ErrorPattern {
739 description: "Parallel processing overhead and contention".to_string(),
740 errortypes: vec!["TimeoutError".to_string(), "ComputationError".to_string()],
741 frequency: 0,
742 common_contexts: vec!["parallel_computing".to_string(), "rayon".to_string()],
743 suggestions: vec![
744 "Adjust thread pool size based on workload".to_string(),
745 "Use work-stealing for better load balancing".to_string(),
746 "Minimize false sharing in parallel algorithms".to_string(),
747 "Consider chunking strategies for better cache locality".to_string(),
748 "Profile thread utilization and adjust accordingly".to_string(),
749 "Use NUMA-aware allocation for large systems".to_string(),
750 ],
751 },
752 ErrorPattern {
753 description: "Data type overflow and underflow in scientific calculations"
754 .to_string(),
755 errortypes: vec!["ValueError".to_string(), "ComputationError".to_string()],
756 frequency: 0,
757 common_contexts: vec![
758 "numerical_integration".to_string(),
759 "statistics".to_string(),
760 ],
761 suggestions: vec![
762 "Use logarithmic computations for very large/small values".to_string(),
763 "Implement numerical scaling and normalization".to_string(),
764 "Use arbitrary precision arithmetic for extreme ranges".to_string(),
765 "Apply Kahan summation for better numerical stability".to_string(),
766 "Check for intermediate overflow in complex calculations".to_string(),
767 ],
768 },
769 ErrorPattern {
770 description: "I/O and serialization failures in scientific data".to_string(),
771 errortypes: vec!["IoError".to_string(), "SerializationError".to_string()],
772 frequency: 0,
773 common_contexts: vec!["data_loading".to_string(), "checkpointing".to_string()],
774 suggestions: vec![
775 "Use streaming I/O for large datasets".to_string(),
776 "Implement progressive loading with error recovery".to_string(),
777 "Use compression to reduce I/O overhead".to_string(),
778 "Implement chunked serialization for large arrays".to_string(),
779 "Use memory-mapped files for random access patterns".to_string(),
780 "Consider distributed storage for very large datasets".to_string(),
781 ],
782 },
783 ]
784 }
785}
786
787impl Default for ErrorDiagnostics {
788 fn default() -> Self {
789 Self::new()
790 }
791}
792
793#[derive(Debug, Clone, Copy, PartialEq, Eq)]
795pub enum PerformanceImpact {
796 Unknown,
797 Low,
798 Medium,
799 High,
800 Critical,
801}
802
803impl fmt::Display for PerformanceImpact {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 match self {
806 Self::Unknown => write!(f, "Unknown"),
807 Self::Low => write!(f, "Low"),
808 Self::Medium => write!(f, "Medium"),
809 Self::High => write!(f, "High"),
810 Self::Critical => write!(f, "Critical"),
811 }
812 }
813}
814
815#[derive(Debug)]
817pub struct ErrorDiagnosticReport {
818 pub error: CoreError,
820 pub environment: Option<EnvironmentInfo>,
822 pub patterns: Vec<ErrorPattern>,
824 pub recent_occurrences: Vec<ErrorOccurrence>,
826 pub performance_impact: PerformanceImpact,
828 pub contextual_suggestions: Vec<String>,
830 pub environment_diagnostics: Vec<String>,
832 pub predictions: Vec<String>,
834 pub domain_strategies: Vec<String>,
836 pub generated_at: SystemTime,
838}
839
840impl ErrorDiagnosticReport {
841 pub fn error(error: CoreError) -> Self {
843 Self {
844 error,
845 environment: None,
846 patterns: Vec::new(),
847 recent_occurrences: Vec::new(),
848 performance_impact: PerformanceImpact::Unknown,
849 contextual_suggestions: Vec::new(),
850 environment_diagnostics: Vec::new(),
851 predictions: Vec::new(),
852 domain_strategies: Vec::new(),
853 generated_at: SystemTime::now(),
854 }
855 }
856
857 pub fn generate_report(&self) -> String {
859 let mut report = String::new();
860
861 report.push_str("🔍 `SciRS2` Error Diagnostic Report\n");
863 report.push_str(&format!("Generated: {:?}\n", self.generated_at));
864 report.push_str("═══════════════════════════════════════════════════════════════\n\n");
865
866 report.push_str("🚨 Error Details:\n");
868 report.push_str(&format!(" {error}\n\n", error = self.error));
869
870 report.push_str(&format!(
872 "⚡ Performance Impact: {}\n\n",
873 self.performance_impact
874 ));
875
876 if let Some(env) = &self.environment {
878 report.push_str("🖥️ Environment Information:\n");
879 report.push_str(&format!(
880 " OS: {os} ({arch})\n",
881 os = env.os,
882 arch = env.arch
883 ));
884 report.push_str(&format!(
885 " `SciRS2` Version: {_version}\n",
886 _version = env.scirs2_version
887 ));
888
889 if let Some(cores) = env.cpu_cores {
890 report.push_str(&format!(" CPU Cores: {cores}\n"));
891 }
892
893 if let Some(memory) = env.available_memory {
894 report.push_str(&format!(
895 " Available Memory: {:.2} GB\n",
896 memory as f64 / 1_000_000_000.0
897 ));
898 }
899
900 if !env.features.is_empty() {
901 report.push_str(&format!(
902 " Enabled Features: {}\n",
903 env.features.join(", ")
904 ));
905 }
906
907 report.push('\n');
908 }
909
910 if !self.environment_diagnostics.is_empty() {
912 report.push_str("🔧 Environment Diagnostics:\n");
913 for diagnostic in &self.environment_diagnostics {
914 report.push_str(&format!(" • {diagnostic}\n"));
915 }
916 report.push('\n');
917 }
918
919 if !self.patterns.is_empty() {
921 report.push_str("📊 Matching Error Patterns:\n");
922 for pattern in &self.patterns {
923 report.push_str(&format!(
924 " • {description}\n",
925 description = pattern.description
926 ));
927 if !pattern.suggestions.is_empty() {
928 report.push_str(" Suggestions:\n");
929 for suggestion in &pattern.suggestions {
930 report.push_str(&format!(" - {suggestion}\n"));
931 }
932 }
933 }
934 report.push('\n');
935 }
936
937 if !self.recent_occurrences.is_empty() {
939 report.push_str(&format!(
940 "📈 Recent Similar Errors: {} in the last 5 minutes\n",
941 self.recent_occurrences.len()
942 ));
943 if self.recent_occurrences.len() > 3 {
944 report.push_str(
945 " ⚠️ High frequency detected - this may indicate a systematic issue\n",
946 );
947 }
948 report.push('\n');
949 }
950
951 if !self.predictions.is_empty() {
953 report.push_str("🔮 Predictive Analysis:\n");
954 for prediction in &self.predictions {
955 report.push_str(&format!(" • {prediction}\n"));
956 }
957 report.push('\n');
958 }
959
960 if !self.domain_strategies.is_empty() {
962 report.push_str("🎯 Domain-Specific Recovery Strategies:\n");
963 for (i, strategy) in self.domain_strategies.iter().enumerate() {
964 report.push_str(&format!(" {num}. {strategy}\n", num = i + 1));
965 }
966 report.push('\n');
967 }
968
969 if !self.contextual_suggestions.is_empty() {
971 report.push_str("💡 Contextual Suggestions:\n");
972 for (i, suggestion) in self.contextual_suggestions.iter().enumerate() {
973 report.push_str(&format!(" {num}. {suggestion}\n", num = i + 1));
974 }
975 report.push('\n');
976 }
977
978 report.push_str("═══════════════════════════════════════════════════════════════\n");
980 report.push_str("For more help, visit: https://github.com/cool-japan/scirs/issues\n");
981
982 report
983 }
984}
985
986impl fmt::Display for ErrorDiagnosticReport {
987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 write!(f, "{}", self.generate_report())
989 }
990}
991
992#[allow(dead_code)]
994pub fn error(err: &CoreError) -> ErrorDiagnosticReport {
995 ErrorDiagnostics::global().analyzeerror(err)
996}
997
998#[allow(dead_code)]
1000pub fn error_with_context(err: &CoreError, context: String) -> ErrorDiagnosticReport {
1001 let diagnostics = ErrorDiagnostics::global();
1002 diagnostics.recorderror(err, context);
1003 diagnostics.analyzeerror(err)
1004}
1005
1006#[macro_export]
1008macro_rules! diagnosticerror {
1009 ($errortype:ident, $message:expr) => {{
1010 let error = $crate::error::CoreError::$errortype($crate::error_context!($message));
1011 let line_num = line!();
1012 let file_name = file!();
1013 let context = format!("line {line_num}, file = {file_name}");
1014 $crate::error::diagnostics::diagnoseerror_with_context(&error, context);
1015 error
1016 }};
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::*;
1022 use crate::ErrorContext;
1023
1024 #[test]
1025 fn test_environment_info() {
1026 let env = EnvironmentInfo::default();
1027 assert!(!env.os.is_empty());
1028 assert!(!env.arch.is_empty());
1029 assert!(!env.scirs2_version.is_empty());
1030 }
1031
1032 #[test]
1033 fn testerror_occurrence() {
1034 let error = CoreError::DomainError(ErrorContext::new("Test error"));
1035 let occurrence = ErrorOccurrence::error(&error, "test_context".to_string())
1036 .with_location("test_function")
1037 .with_metadata("key", "value");
1038
1039 assert_eq!(occurrence.errortype, "DomainError");
1040 assert_eq!(occurrence.context, "test_context");
1041 assert_eq!(occurrence.location, Some("test_function".to_string()));
1042 assert_eq!(occurrence.metadata.get("key"), Some(&"value".to_string()));
1043 }
1044
1045 #[test]
1046 fn test_diagnosticserror_diagnostics() {
1047 let diagnostics = ErrorDiagnostics::new();
1048 let error = CoreError::MemoryError(ErrorContext::new("Out of memory"));
1049
1050 let report = diagnostics.analyzeerror(&error);
1051 assert!(matches!(report.error, CoreError::MemoryError(_)));
1052 assert!(matches!(report.performance_impact, PerformanceImpact::High));
1053 }
1054
1055 #[test]
1056 fn test_diagnostic_report_generation() {
1057 let error = CoreError::ShapeError(ErrorContext::new("Shape mismatch"));
1058 let report = ErrorDiagnostics::global().analyzeerror(&error);
1059
1060 let report_string = report.generate_report();
1061 assert!(report_string.contains("Error Diagnostic Report"));
1062 assert!(report_string.contains("Shape mismatch"));
1063 }
1064}