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