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(&"linalg".to_string()) {
475 diagnostics
476 .push("Using OxiBLAS backend (pure Rust) - no system dependencies".to_string());
477 }
478
479 if self.environment.features.contains(&"gpu".to_string()) {
481 diagnostics.push("GPU acceleration available".to_string());
482 if self.environment.features.contains(&"cuda".to_string()) {
483 diagnostics.push("CUDA backend enabled for NVIDIA GPUs".to_string());
484 }
485 }
486 }
487
488 #[allow(dead_code)]
490 pub fn predict_potentialerrors(&self, context: &str) -> Vec<String> {
491 let mut predictions = Vec::new();
492 let history = self.error_history.lock().expect("Operation failed");
493
494 let mut error_counts: HashMap<String, usize> = HashMap::new();
496 let recent_cutoff = SystemTime::now() - Duration::from_secs(3600); for occurrence in history.iter() {
499 if occurrence.timestamp >= recent_cutoff {
500 *error_counts
501 .entry(occurrence.errortype.clone())
502 .or_insert(0) += 1;
503 }
504 }
505
506 for (errortype, count) in error_counts {
508 if count >= 3 {
509 predictions.push(format!(
510 "High risk of {errortype} based on recent frequency ({count}x in last hour)"
511 ));
512 }
513 }
514
515 if (context.contains("matrix") || context.contains("linear_algebra"))
517 && self
518 .environment
519 .available_memory
520 .is_some_and(|mem| mem < 8_000_000_000)
521 {
522 predictions.push("Potential memory issues with large matrix operations".to_string());
523 }
524
525 if context.contains("optimization") || context.contains("solver") {
526 predictions.push(
527 "Potential convergence issues - consider robust initial conditions".to_string(),
528 );
529 }
530
531 if context.contains("parallel") && self.environment.cpu_cores == Some(1) {
532 predictions
533 .push("Parallel algorithms may not be effective on single-core system".to_string());
534 }
535
536 predictions
537 }
538
539 #[allow(dead_code)]
541 pub fn suggest_domain_recovery(&self, error: &CoreError, domain: &str) -> Vec<String> {
542 let mut strategies = Vec::new();
543
544 match domain {
545 "linear_algebra" => match error {
546 CoreError::MemoryError(_) => {
547 strategies.extend(vec![
548 "Use iterative solvers instead of direct factorization".to_string(),
549 "Implement block algorithms for large matrices".to_string(),
550 "Consider sparse matrix representations".to_string(),
551 "Use out-of-core matrix algorithms".to_string(),
552 ]);
553 }
554 CoreError::ConvergenceError(_) => {
555 strategies.extend(vec![
556 "Apply preconditioning (ILU, SSOR, or Jacobi)".to_string(),
557 "Use more robust factorization (SVD instead of LU)".to_string(),
558 "Check matrix conditioning and apply regularization".to_string(),
559 "Try different solver algorithms (GMRES, BiCGSTAB)".to_string(),
560 ]);
561 }
562 _ => {}
563 },
564
565 "optimization" => match error {
566 CoreError::ConvergenceError(_) => {
567 strategies.extend(vec![
568 "Use trust region methods for better global convergence".to_string(),
569 "Apply line search algorithms with backtracking".to_string(),
570 "Try multiple random starting points".to_string(),
571 "Use gradient-free methods for noisy objectives".to_string(),
572 "Implement adaptive step size control".to_string(),
573 ]);
574 }
575 CoreError::DomainError(_) => {
576 strategies.extend(vec![
577 "Add constraint handling and projection operators".to_string(),
578 "Use barrier methods for constrained optimization".to_string(),
579 "Implement bounds checking and clipping".to_string(),
580 ]);
581 }
582 _ => {}
583 },
584
585 "statistics" => match error {
586 CoreError::DomainError(_) => {
587 strategies.extend(vec![
588 "Use robust statistical methods for outliers".to_string(),
589 "Apply data transformation (log, Box-Cox)".to_string(),
590 "Implement missing data handling strategies".to_string(),
591 "Use non-parametric methods for non-normal data".to_string(),
592 ]);
593 }
594 CoreError::ComputationError(_) => {
595 strategies.extend(vec![
596 "Use numerically stable algorithms (Welford for variance)".to_string(),
597 "Apply importance sampling for rare events".to_string(),
598 "Use bootstrap methods for robust estimates".to_string(),
599 ]);
600 }
601 _ => {}
602 },
603
604 "signal_processing" => match error {
605 CoreError::MemoryError(_) => {
606 strategies.extend(vec![
607 "Use streaming FFT algorithms".to_string(),
608 "Implement overlap-add/overlap-save methods".to_string(),
609 "Use decimation for reduced sample rates".to_string(),
610 ]);
611 }
612 CoreError::ComputationError(_) => {
613 strategies.extend(vec![
614 "Use windowing to reduce spectral leakage".to_string(),
615 "Apply zero-padding for better frequency resolution".to_string(),
616 "Use advanced spectral estimation methods".to_string(),
617 ]);
618 }
619 _ => {}
620 },
621 _ => {
622 strategies.push("Consider using more robust numerical algorithms".to_string());
624 strategies.push("Implement error checking and data validation".to_string());
625 strategies.push("Use iterative refinement for better accuracy".to_string());
626 }
627 }
628
629 strategies
630 }
631
632 fn initialize_patterns() -> Vec<ErrorPattern> {
634 vec![
635 ErrorPattern {
636 description: "Memory allocation failures in large matrix operations".to_string(),
637 errortypes: vec!["MemoryError".to_string()],
638 frequency: 0,
639 common_contexts: vec![
640 "matrix_multiplication".to_string(),
641 "decomposition".to_string(),
642 ],
643 suggestions: vec![
644 "Use chunked processing for large matrices".to_string(),
645 "Consider using f32 instead of f64 to reduce memory usage".to_string(),
646 "Enable out-of-core algorithms if available".to_string(),
647 "Use memory-mapped arrays for very large datasets".to_string(),
648 "Consider distributed computing for extremely large problems".to_string(),
649 ],
650 },
651 ErrorPattern {
652 description: "Convergence failures in iterative algorithms".to_string(),
653 errortypes: vec!["ConvergenceError".to_string()],
654 frequency: 0,
655 common_contexts: vec!["optimization".to_string(), "linear_solver".to_string()],
656 suggestions: vec![
657 "Increase maximum iteration count".to_string(),
658 "Adjust convergence tolerance".to_string(),
659 "Try different initial conditions".to_string(),
660 "Use preconditioning to improve convergence".to_string(),
661 "Consider adaptive step sizes or trust region methods".to_string(),
662 "Check problem conditioning and scaling".to_string(),
663 ],
664 },
665 ErrorPattern {
666 description: "Shape mismatches in array operations".to_string(),
667 errortypes: vec!["ShapeError".to_string(), "DimensionError".to_string()],
668 frequency: 0,
669 common_contexts: vec!["matrix_operations".to_string(), "broadcasting".to_string()],
670 suggestions: vec![
671 "Check input array shapes before operations".to_string(),
672 "Use reshaping or broadcasting to make arrays compatible".to_string(),
673 "Verify matrix multiplication dimension compatibility (A: m×k, B: k×n)"
674 .to_string(),
675 "Consider using automatic broadcasting utilities".to_string(),
676 "Use array protocol for mixed array type compatibility".to_string(),
677 ],
678 },
679 ErrorPattern {
680 description: "Domain errors with mathematical functions".to_string(),
681 errortypes: vec!["DomainError".to_string()],
682 frequency: 0,
683 common_contexts: vec!["special_functions".to_string(), "statistics".to_string()],
684 suggestions: vec![
685 "Check input ranges for mathematical functions".to_string(),
686 "Handle edge cases (zero, negative values, infinities)".to_string(),
687 "Use input validation before calling functions".to_string(),
688 "Consider using robust numerical methods for ill-conditioned problems"
689 .to_string(),
690 "Use IEEE 754 special value handling where appropriate".to_string(),
691 ],
692 },
693 ErrorPattern {
695 description: "GPU memory exhaustion in accelerated computations".to_string(),
696 errortypes: vec!["MemoryError".to_string(), "ComputationError".to_string()],
697 frequency: 0,
698 common_contexts: vec![
699 "gpu_acceleration".to_string(),
700 "neural_networks".to_string(),
701 ],
702 suggestions: vec![
703 "Reduce batch size to fit GPU memory".to_string(),
704 "Use gradient accumulation for large batches".to_string(),
705 "Enable mixed precision training (fp16/fp32)".to_string(),
706 "Consider model parallelism for very large models".to_string(),
707 "Use CPU fallback for computations that don't fit on GPU".to_string(),
708 ],
709 },
710 ErrorPattern {
711 description: "Numerical instability in scientific computations".to_string(),
712 errortypes: vec![
713 "ComputationError".to_string(),
714 "ConvergenceError".to_string(),
715 ],
716 frequency: 0,
717 common_contexts: vec![
718 "linear_algebra".to_string(),
719 "ode_solving".to_string(),
720 "pde_solving".to_string(),
721 ],
722 suggestions: vec![
723 "Use higher precision (f64 instead of f32)".to_string(),
724 "Apply numerical stabilization techniques".to_string(),
725 "Check condition numbers and matrix rank".to_string(),
726 "Use pivoting strategies in decompositions".to_string(),
727 "Consider regularization for ill-posed problems".to_string(),
728 "Use iterative refinement for better accuracy".to_string(),
729 ],
730 },
731 ErrorPattern {
732 description: "Parallel processing overhead and contention".to_string(),
733 errortypes: vec!["TimeoutError".to_string(), "ComputationError".to_string()],
734 frequency: 0,
735 common_contexts: vec!["parallel_computing".to_string(), "rayon".to_string()],
736 suggestions: vec![
737 "Adjust thread pool size based on workload".to_string(),
738 "Use work-stealing for better load balancing".to_string(),
739 "Minimize false sharing in parallel algorithms".to_string(),
740 "Consider chunking strategies for better cache locality".to_string(),
741 "Profile thread utilization and adjust accordingly".to_string(),
742 "Use NUMA-aware allocation for large systems".to_string(),
743 ],
744 },
745 ErrorPattern {
746 description: "Data type overflow and underflow in scientific calculations"
747 .to_string(),
748 errortypes: vec!["ValueError".to_string(), "ComputationError".to_string()],
749 frequency: 0,
750 common_contexts: vec![
751 "numerical_integration".to_string(),
752 "statistics".to_string(),
753 ],
754 suggestions: vec![
755 "Use logarithmic computations for very large/small values".to_string(),
756 "Implement numerical scaling and normalization".to_string(),
757 "Use arbitrary precision arithmetic for extreme ranges".to_string(),
758 "Apply Kahan summation for better numerical stability".to_string(),
759 "Check for intermediate overflow in complex calculations".to_string(),
760 ],
761 },
762 ErrorPattern {
763 description: "I/O and serialization failures in scientific data".to_string(),
764 errortypes: vec!["IoError".to_string(), "SerializationError".to_string()],
765 frequency: 0,
766 common_contexts: vec!["data_loading".to_string(), "checkpointing".to_string()],
767 suggestions: vec![
768 "Use streaming I/O for large datasets".to_string(),
769 "Implement progressive loading with error recovery".to_string(),
770 "Use compression to reduce I/O overhead".to_string(),
771 "Implement chunked serialization for large arrays".to_string(),
772 "Use memory-mapped files for random access patterns".to_string(),
773 "Consider distributed storage for very large datasets".to_string(),
774 ],
775 },
776 ]
777 }
778}
779
780impl Default for ErrorDiagnostics {
781 fn default() -> Self {
782 Self::new()
783 }
784}
785
786#[derive(Debug, Clone, Copy, PartialEq, Eq)]
788pub enum PerformanceImpact {
789 Unknown,
790 Low,
791 Medium,
792 High,
793 Critical,
794}
795
796impl fmt::Display for PerformanceImpact {
797 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
798 match self {
799 Self::Unknown => write!(f, "Unknown"),
800 Self::Low => write!(f, "Low"),
801 Self::Medium => write!(f, "Medium"),
802 Self::High => write!(f, "High"),
803 Self::Critical => write!(f, "Critical"),
804 }
805 }
806}
807
808#[derive(Debug)]
810pub struct ErrorDiagnosticReport {
811 pub error: CoreError,
813 pub environment: Option<EnvironmentInfo>,
815 pub patterns: Vec<ErrorPattern>,
817 pub recent_occurrences: Vec<ErrorOccurrence>,
819 pub performance_impact: PerformanceImpact,
821 pub contextual_suggestions: Vec<String>,
823 pub environment_diagnostics: Vec<String>,
825 pub predictions: Vec<String>,
827 pub domain_strategies: Vec<String>,
829 pub generated_at: SystemTime,
831}
832
833impl ErrorDiagnosticReport {
834 pub fn error(error: CoreError) -> Self {
836 Self {
837 error,
838 environment: None,
839 patterns: Vec::new(),
840 recent_occurrences: Vec::new(),
841 performance_impact: PerformanceImpact::Unknown,
842 contextual_suggestions: Vec::new(),
843 environment_diagnostics: Vec::new(),
844 predictions: Vec::new(),
845 domain_strategies: Vec::new(),
846 generated_at: SystemTime::now(),
847 }
848 }
849
850 pub fn generate_report(&self) -> String {
852 let mut report = String::new();
853
854 report.push_str("🔍 `SciRS2` Error Diagnostic Report\n");
856 report.push_str(&format!("Generated: {:?}\n", self.generated_at));
857 report.push_str("═══════════════════════════════════════════════════════════════\n\n");
858
859 report.push_str("🚨 Error Details:\n");
861 report.push_str(&format!(" {error}\n\n", error = self.error));
862
863 report.push_str(&format!(
865 "⚡ Performance Impact: {}\n\n",
866 self.performance_impact
867 ));
868
869 if let Some(env) = &self.environment {
871 report.push_str("🖥️ Environment Information:\n");
872 report.push_str(&format!(
873 " OS: {os} ({arch})\n",
874 os = env.os,
875 arch = env.arch
876 ));
877 report.push_str(&format!(
878 " `SciRS2` Version: {_version}\n",
879 _version = env.scirs2_version
880 ));
881
882 if let Some(cores) = env.cpu_cores {
883 report.push_str(&format!(" CPU Cores: {cores}\n"));
884 }
885
886 if let Some(memory) = env.available_memory {
887 report.push_str(&format!(
888 " Available Memory: {:.2} GB\n",
889 memory as f64 / 1_000_000_000.0
890 ));
891 }
892
893 if !env.features.is_empty() {
894 report.push_str(&format!(
895 " Enabled Features: {}\n",
896 env.features.join(", ")
897 ));
898 }
899
900 report.push('\n');
901 }
902
903 if !self.environment_diagnostics.is_empty() {
905 report.push_str("🔧 Environment Diagnostics:\n");
906 for diagnostic in &self.environment_diagnostics {
907 report.push_str(&format!(" • {diagnostic}\n"));
908 }
909 report.push('\n');
910 }
911
912 if !self.patterns.is_empty() {
914 report.push_str("📊 Matching Error Patterns:\n");
915 for pattern in &self.patterns {
916 report.push_str(&format!(
917 " • {description}\n",
918 description = pattern.description
919 ));
920 if !pattern.suggestions.is_empty() {
921 report.push_str(" Suggestions:\n");
922 for suggestion in &pattern.suggestions {
923 report.push_str(&format!(" - {suggestion}\n"));
924 }
925 }
926 }
927 report.push('\n');
928 }
929
930 if !self.recent_occurrences.is_empty() {
932 report.push_str(&format!(
933 "📈 Recent Similar Errors: {} in the last 5 minutes\n",
934 self.recent_occurrences.len()
935 ));
936 if self.recent_occurrences.len() > 3 {
937 report.push_str(
938 " ⚠️ High frequency detected - this may indicate a systematic issue\n",
939 );
940 }
941 report.push('\n');
942 }
943
944 if !self.predictions.is_empty() {
946 report.push_str("🔮 Predictive Analysis:\n");
947 for prediction in &self.predictions {
948 report.push_str(&format!(" • {prediction}\n"));
949 }
950 report.push('\n');
951 }
952
953 if !self.domain_strategies.is_empty() {
955 report.push_str("🎯 Domain-Specific Recovery Strategies:\n");
956 for (i, strategy) in self.domain_strategies.iter().enumerate() {
957 report.push_str(&format!(" {num}. {strategy}\n", num = i + 1));
958 }
959 report.push('\n');
960 }
961
962 if !self.contextual_suggestions.is_empty() {
964 report.push_str("💡 Contextual Suggestions:\n");
965 for (i, suggestion) in self.contextual_suggestions.iter().enumerate() {
966 report.push_str(&format!(" {num}. {suggestion}\n", num = i + 1));
967 }
968 report.push('\n');
969 }
970
971 report.push_str("═══════════════════════════════════════════════════════════════\n");
973 report.push_str("For more help, visit: https://github.com/cool-japan/scirs/issues\n");
974
975 report
976 }
977}
978
979impl fmt::Display for ErrorDiagnosticReport {
980 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981 write!(f, "{}", self.generate_report())
982 }
983}
984
985#[allow(dead_code)]
987pub fn error(err: &CoreError) -> ErrorDiagnosticReport {
988 ErrorDiagnostics::global().analyzeerror(err)
989}
990
991#[allow(dead_code)]
993pub fn error_with_context(err: &CoreError, context: String) -> ErrorDiagnosticReport {
994 let diagnostics = ErrorDiagnostics::global();
995 diagnostics.recorderror(err, context);
996 diagnostics.analyzeerror(err)
997}
998
999#[macro_export]
1001macro_rules! diagnosticerror {
1002 ($errortype:ident, $message:expr) => {{
1003 let error = $crate::error::CoreError::$errortype($crate::error_context!($message));
1004 let line_num = line!();
1005 let file_name = file!();
1006 let context = format!("line {line_num}, file = {file_name}");
1007 $crate::error::diagnostics::diagnoseerror_with_context(&error, context);
1008 error
1009 }};
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::*;
1015 use crate::ErrorContext;
1016
1017 #[test]
1018 fn test_environment_info() {
1019 let env = EnvironmentInfo::default();
1020 assert!(!env.os.is_empty());
1021 assert!(!env.arch.is_empty());
1022 assert!(!env.scirs2_version.is_empty());
1023 }
1024
1025 #[test]
1026 fn testerror_occurrence() {
1027 let error = CoreError::DomainError(ErrorContext::new("Test error"));
1028 let occurrence = ErrorOccurrence::error(&error, "test_context".to_string())
1029 .with_location("test_function")
1030 .with_metadata("key", "value");
1031
1032 assert_eq!(occurrence.errortype, "DomainError");
1033 assert_eq!(occurrence.context, "test_context");
1034 assert_eq!(occurrence.location, Some("test_function".to_string()));
1035 assert_eq!(occurrence.metadata.get("key"), Some(&"value".to_string()));
1036 }
1037
1038 #[test]
1039 fn test_diagnosticserror_diagnostics() {
1040 let diagnostics = ErrorDiagnostics::new();
1041 let error = CoreError::MemoryError(ErrorContext::new("Out of memory"));
1042
1043 let report = diagnostics.analyzeerror(&error);
1044 assert!(matches!(report.error, CoreError::MemoryError(_)));
1045 assert!(matches!(report.performance_impact, PerformanceImpact::High));
1046 }
1047
1048 #[test]
1049 fn test_diagnostic_report_generation() {
1050 let error = CoreError::ShapeError(ErrorContext::new("Shape mismatch"));
1051 let report = ErrorDiagnostics::global().analyzeerror(&error);
1052
1053 let report_string = report.generate_report();
1054 assert!(report_string.contains("Error Diagnostic Report"));
1055 assert!(report_string.contains("Shape mismatch"));
1056 }
1057}