1#![allow(dead_code)]
8#![allow(unexpected_cfgs)]
9use crate::{JitError, JitResult};
10use std::collections::HashMap;
11use std::fmt;
12use std::time::Instant;
13
14#[derive(Debug)]
16pub struct ErrorDiagnosticsManager {
17 config: DiagnosticsConfig,
19
20 error_history: Vec<DiagnosticError>,
22
23 error_patterns: HashMap<String, ErrorPattern>,
25
26 recovery_suggestions: HashMap<ErrorCategory, Vec<RecoverySuggestion>>,
28
29 context_stack: Vec<DiagnosticContext>,
31
32 stats: DiagnosticsStats,
34}
35
36#[derive(Debug)]
38pub struct DiagnosticError {
39 pub id: String,
41
42 pub timestamp: Instant,
44
45 pub category: ErrorCategory,
47
48 pub severity: ErrorSeverity,
50
51 pub message: String,
53
54 pub source_location: Option<SourceLocation>,
56
57 pub stack_trace: Vec<StackFrame>,
59
60 pub context: DiagnosticContext,
62
63 pub related_errors: Vec<String>,
65
66 pub suggestions: Vec<RecoverySuggestion>,
68
69 pub metadata: HashMap<String, String>,
71
72 pub underlying_error: Option<Box<JitError>>,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Hash)]
78pub enum ErrorCategory {
79 GraphConstruction,
81
82 TypeInference,
84
85 ShapeInference,
87
88 Optimization,
90
91 CodeGeneration,
93
94 Runtime,
96
97 Memory,
99
100 Resource,
102
103 UserInput,
105
106 Internal,
108
109 External,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
115pub enum ErrorSeverity {
116 Info,
118
119 Warning,
121
122 Error,
124
125 Fatal,
127
128 Ice, }
131
132#[derive(Debug, Clone)]
134pub struct SourceLocation {
135 pub file: String,
137
138 pub line: u32,
140
141 pub column: u32,
143
144 pub length: Option<u32>,
146
147 pub snippet: Option<String>,
149}
150
151#[derive(Debug, Clone)]
153pub struct StackFrame {
154 pub function: String,
156
157 pub file: Option<String>,
159
160 pub line: Option<u32>,
162
163 pub address: Option<u64>,
165
166 pub module: Option<String>,
168}
169
170#[derive(Debug, Clone)]
172pub struct DiagnosticContext {
173 pub operation: String,
175
176 pub input: String,
178
179 pub expected: Option<String>,
181
182 pub actual: Option<String>,
184
185 pub environment: EnvironmentInfo,
187
188 pub data: HashMap<String, String>,
190}
191
192#[derive(Debug, Clone)]
194pub struct EnvironmentInfo {
195 pub rust_version: String,
197
198 pub torsh_version: String,
200
201 pub target_arch: String,
203
204 pub target_os: String,
206
207 pub available_memory: Option<u64>,
209
210 pub cpu_info: Option<String>,
212
213 pub gpu_info: Option<String>,
215}
216
217#[derive(Debug, Clone)]
219pub struct ErrorPattern {
220 pub name: String,
222
223 pub description: String,
225
226 pub criteria: Vec<MatchCriterion>,
228
229 pub common_causes: Vec<String>,
231
232 pub solutions: Vec<RecoverySuggestion>,
234
235 pub frequency: u64,
237}
238
239#[derive(Debug, Clone)]
241pub enum MatchCriterion {
242 MessageContains(String),
244
245 CategoryEquals(ErrorCategory),
247
248 LocationMatches(String),
250
251 StackContains(String),
253
254 Custom(fn(&DiagnosticError) -> bool),
256}
257
258#[derive(Debug, Clone)]
260pub struct RecoverySuggestion {
261 pub suggestion_type: SuggestionType,
263
264 pub message: String,
266
267 pub explanation: Option<String>,
269
270 pub code_example: Option<String>,
272
273 pub doc_link: Option<String>,
275
276 pub confidence: f32,
278
279 pub auto_fix: Option<AutoFix>,
281}
282
283#[derive(Debug, Clone)]
285pub enum SuggestionType {
286 QuickFix,
288
289 CodeChange,
291
292 ConfigChange,
294
295 EnvironmentSetup,
297
298 Documentation,
300
301 Workaround,
303
304 Investigation,
306}
307
308#[derive(Debug, Clone)]
310pub struct AutoFix {
311 pub description: String,
313
314 pub fix_fn: fn(&DiagnosticError) -> JitResult<()>,
316
317 pub side_effects: Vec<String>,
319
320 pub requires_confirmation: bool,
322}
323
324#[derive(Debug, Clone)]
326pub struct DiagnosticsConfig {
327 pub enabled: bool,
329
330 pub max_history_size: usize,
332
333 pub collect_stack_traces: bool,
335
336 pub extract_source_snippets: bool,
338
339 pub reporting_level: ErrorSeverity,
341
342 pub enable_pattern_matching: bool,
344
345 pub enable_suggestions: bool,
347
348 pub max_suggestions: usize,
350
351 pub color_output: bool,
353
354 pub verbose: bool,
356}
357
358#[derive(Debug, Clone, Default)]
360pub struct DiagnosticsStats {
361 pub total_errors: u64,
363
364 pub errors_by_category: HashMap<ErrorCategory, u64>,
366
367 pub errors_by_severity: HashMap<ErrorSeverity, u64>,
369
370 pub pattern_matches: u64,
372
373 pub suggestions_provided: u64,
375
376 pub auto_fixes_applied: u64,
378}
379
380pub struct ErrorFormatter {
382 config: FormatterConfig,
384}
385
386#[derive(Debug, Clone)]
388pub struct FormatterConfig {
389 pub include_source: bool,
391
392 pub include_stack_trace: bool,
394
395 pub include_suggestions: bool,
397
398 pub use_colors: bool,
400
401 pub max_line_length: usize,
403
404 pub indent_size: usize,
406}
407
408impl Default for DiagnosticsConfig {
409 fn default() -> Self {
410 Self {
411 enabled: true,
412 max_history_size: 1000,
413 collect_stack_traces: true,
414 extract_source_snippets: true,
415 reporting_level: ErrorSeverity::Warning,
416 enable_pattern_matching: true,
417 enable_suggestions: true,
418 max_suggestions: 5,
419 color_output: true,
420 verbose: false,
421 }
422 }
423}
424
425impl Default for FormatterConfig {
426 fn default() -> Self {
427 Self {
428 include_source: true,
429 include_stack_trace: true,
430 include_suggestions: true,
431 use_colors: true,
432 max_line_length: 120,
433 indent_size: 2,
434 }
435 }
436}
437
438impl ErrorDiagnosticsManager {
439 pub fn new(config: DiagnosticsConfig) -> Self {
441 let mut manager = Self {
442 config,
443 error_history: Vec::new(),
444 error_patterns: HashMap::new(),
445 recovery_suggestions: HashMap::new(),
446 context_stack: Vec::new(),
447 stats: DiagnosticsStats::default(),
448 };
449
450 manager.initialize_default_patterns();
451 manager.initialize_default_suggestions();
452 manager
453 }
454
455 pub fn with_defaults() -> Self {
457 Self::new(DiagnosticsConfig::default())
458 }
459
460 pub fn record_error(&mut self, error: JitError) -> DiagnosticError {
462 let mut diagnostic_error = self.create_diagnostic_error(error);
463
464 self.stats.total_errors += 1;
466 *self
467 .stats
468 .errors_by_category
469 .entry(diagnostic_error.category.clone())
470 .or_insert(0) += 1;
471 *self
472 .stats
473 .errors_by_severity
474 .entry(diagnostic_error.severity.clone())
475 .or_insert(0) += 1;
476
477 if self.config.enable_pattern_matching {
479 self.match_error_patterns(&diagnostic_error);
480 }
481
482 if self.config.enable_suggestions {
484 self.add_recovery_suggestions(&mut diagnostic_error);
485 }
486
487 let diagnostic_error_copy = DiagnosticError {
489 id: diagnostic_error.id.clone(),
490 timestamp: diagnostic_error.timestamp,
491 category: diagnostic_error.category.clone(),
492 severity: diagnostic_error.severity.clone(),
493 message: diagnostic_error.message.clone(),
494 source_location: diagnostic_error.source_location.clone(),
495 stack_trace: diagnostic_error.stack_trace.clone(),
496 context: diagnostic_error.context.clone(),
497 related_errors: diagnostic_error.related_errors.clone(),
498 suggestions: diagnostic_error.suggestions.clone(),
499 metadata: diagnostic_error.metadata.clone(),
500 underlying_error: None, };
502
503 if self.error_history.len() >= self.config.max_history_size {
504 self.error_history.remove(0);
505 }
506 self.error_history.push(diagnostic_error_copy);
507
508 diagnostic_error
509 }
510
511 fn create_diagnostic_error(&self, error: JitError) -> DiagnosticError {
513 let error_id = format!("err_{}", self.stats.total_errors);
514 let category = self.categorize_error(&error);
515 let severity = self.determine_severity(&error);
516 let message = error.to_string();
517
518 let context = self
519 .context_stack
520 .last()
521 .cloned()
522 .unwrap_or_else(|| DiagnosticContext {
523 operation: "unknown".to_string(),
524 input: "unknown".to_string(),
525 expected: None,
526 actual: None,
527 environment: self.get_environment_info(),
528 data: HashMap::new(),
529 });
530
531 DiagnosticError {
532 id: error_id,
533 timestamp: Instant::now(),
534 category,
535 severity,
536 message,
537 source_location: None,
538 stack_trace: self.collect_stack_trace(),
539 context,
540 related_errors: Vec::new(),
541 suggestions: Vec::new(),
542 metadata: HashMap::new(),
543 underlying_error: Some(Box::new(error)),
544 }
545 }
546
547 fn categorize_error(&self, error: &JitError) -> ErrorCategory {
549 match error {
550 JitError::GraphError(_) => ErrorCategory::GraphConstruction,
551 JitError::OptimizationError(_) => ErrorCategory::Optimization,
552 JitError::CodeGenError(_) => ErrorCategory::CodeGeneration,
553 JitError::RuntimeError(_) => ErrorCategory::Runtime,
554 JitError::UnsupportedOp(_) => ErrorCategory::UserInput,
555 JitError::CompilationError(_) => ErrorCategory::CodeGeneration,
556 JitError::AnalysisError(_) => ErrorCategory::TypeInference,
557 JitError::BackendError(_) => ErrorCategory::External,
558 JitError::FusionError(_) => ErrorCategory::Optimization,
559 JitError::AbstractInterpretationError(_) => ErrorCategory::TypeInference,
560 JitError::NotImplemented(_) => ErrorCategory::UserInput,
561 }
562 }
563
564 fn determine_severity(&self, error: &JitError) -> ErrorSeverity {
566 match error {
567 JitError::GraphError(_) => ErrorSeverity::Error,
568 JitError::OptimizationError(_) => ErrorSeverity::Warning,
569 JitError::CodeGenError(_) => ErrorSeverity::Error,
570 JitError::RuntimeError(_) => ErrorSeverity::Error,
571 JitError::UnsupportedOp(_) => ErrorSeverity::Error,
572 JitError::CompilationError(_) => ErrorSeverity::Error,
573 JitError::AnalysisError(_) => ErrorSeverity::Warning,
574 JitError::BackendError(_) => ErrorSeverity::Fatal,
575 JitError::FusionError(_) => ErrorSeverity::Warning,
576 JitError::AbstractInterpretationError(_) => ErrorSeverity::Warning,
577 JitError::NotImplemented(_) => ErrorSeverity::Error,
578 }
579 }
580
581 fn collect_stack_trace(&self) -> Vec<StackFrame> {
583 if !self.config.collect_stack_traces {
584 return Vec::new();
585 }
586
587 #[cfg(feature = "std_backtrace")]
589 {
590 use std::backtrace::{Backtrace, BacktraceStatus};
591 let bt = Backtrace::capture();
592 if bt.status() == BacktraceStatus::Captured {
593 let bt_str = format!("{:?}", bt);
595 return self.parse_backtrace_string(&bt_str);
596 }
597 }
598
599 let mut frames = Vec::new();
601
602 let thread = std::thread::current();
604 frames.push(StackFrame {
605 function: thread.name().unwrap_or("unknown").to_string(),
606 file: None,
607 line: None,
608 address: None,
609 module: Some("torsh_jit".to_string()),
610 });
611
612 frames
613 }
614
615 #[cfg(feature = "std_backtrace")]
617 fn parse_backtrace_string(&self, backtrace: &str) -> Vec<StackFrame> {
618 let mut frames = Vec::new();
619 for line in backtrace.lines().take(20) {
620 if let Some(function) = line.split("::").last() {
622 frames.push(StackFrame {
623 function: function.trim().to_string(),
624 file: None,
625 line: None,
626 address: None,
627 module: Some("torsh_jit".to_string()),
628 });
629 }
630 }
631 frames
632 }
633
634 fn get_environment_info(&self) -> EnvironmentInfo {
636 EnvironmentInfo {
637 rust_version: self.get_rust_version(),
638 torsh_version: env!("CARGO_PKG_VERSION").to_string(),
639 target_arch: std::env::consts::ARCH.to_string(),
640 target_os: std::env::consts::OS.to_string(),
641 available_memory: self.get_available_memory(),
642 cpu_info: self.get_cpu_info(),
643 gpu_info: self.get_gpu_info(),
644 }
645 }
646
647 fn get_rust_version(&self) -> String {
649 std::env::var("RUSTC_VERSION")
651 .unwrap_or_else(|_| env!("CARGO_PKG_RUST_VERSION").to_string())
652 }
653
654 fn get_available_memory(&self) -> Option<u64> {
656 #[cfg(target_os = "linux")]
657 {
658 if let Ok(contents) = std::fs::read_to_string("/proc/meminfo") {
659 for line in contents.lines() {
660 if line.starts_with("MemTotal:") {
661 if let Some(kb_str) = line.split_whitespace().nth(1) {
662 if let Ok(kb) = kb_str.parse::<u64>() {
663 return Some(kb * 1024); }
665 }
666 }
667 }
668 }
669 }
670
671 #[cfg(target_os = "macos")]
672 {
673 use std::process::Command;
674 if let Ok(output) = Command::new("sysctl").arg("hw.memsize").output() {
675 if let Ok(text) = String::from_utf8(output.stdout) {
676 if let Some(size) = text.split(':').nth(1) {
677 if let Ok(bytes) = size.trim().parse::<u64>() {
678 return Some(bytes);
679 }
680 }
681 }
682 }
683 }
684
685 None
686 }
687
688 fn get_cpu_info(&self) -> Option<String> {
690 #[cfg(target_os = "linux")]
691 {
692 if let Ok(contents) = std::fs::read_to_string("/proc/cpuinfo") {
693 for line in contents.lines() {
694 if line.starts_with("model name") {
695 if let Some(name) = line.split(':').nth(1) {
696 return Some(name.trim().to_string());
697 }
698 }
699 }
700 }
701 }
702
703 #[cfg(target_os = "macos")]
704 {
705 use std::process::Command;
706 if let Ok(output) = Command::new("sysctl")
707 .arg("-n")
708 .arg("machdep.cpu.brand_string")
709 .output()
710 {
711 if let Ok(cpu) = String::from_utf8(output.stdout) {
712 return Some(cpu.trim().to_string());
713 }
714 }
715 }
716
717 Some(format!("{} core(s)", num_cpus::get()))
718 }
719
720 fn get_gpu_info(&self) -> Option<String> {
722 #[cfg(feature = "gpu")]
723 {
724 Some("GPU support enabled".to_string())
727 }
728
729 #[cfg(not(feature = "gpu"))]
730 {
731 None
732 }
733 }
734
735 fn match_error_patterns(&mut self, error: &DiagnosticError) {
737 let mut matched_patterns = Vec::new();
738 for (pattern_name, pattern) in &self.error_patterns {
739 if self.matches_pattern(error, pattern) {
740 self.stats.pattern_matches += 1;
741 matched_patterns.push((pattern_name.clone(), pattern.clone()));
742 }
743 }
744
745 for (_pattern_name, _pattern) in matched_patterns {
748 }
750 }
751
752 fn apply_pattern_handling(
754 &mut self,
755 error: &mut DiagnosticError,
756 pattern_name: &str,
757 pattern: &ErrorPattern,
758 ) {
759 for suggestion in &pattern.solutions {
761 error.suggestions.push(suggestion.clone());
762 }
763
764 for cause in &pattern.common_causes {
766 error
767 .related_errors
768 .push(format!("Common cause: {}", cause));
769 }
770
771 if let Some(ref ctx) = self.context_stack.last() {
773 error.related_errors.push(format!(
774 "Pattern '{}' matched in context: {}",
775 pattern_name, ctx.operation
776 ));
777 }
778
779 self.stats.suggestions_provided += pattern.solutions.len() as u64;
781
782 if pattern.frequency > 100 {
784 if error.severity == ErrorSeverity::Error {
786 error.severity = ErrorSeverity::Warning;
787 }
788 }
789 }
790
791 fn matches_pattern(&self, error: &DiagnosticError, pattern: &ErrorPattern) -> bool {
793 for criterion in &pattern.criteria {
794 match criterion {
795 MatchCriterion::MessageContains(text) => {
796 if !error.message.contains(text) {
797 return false;
798 }
799 }
800 MatchCriterion::CategoryEquals(category) => {
801 if error.category != *category {
802 return false;
803 }
804 }
805 MatchCriterion::LocationMatches(pattern) => {
806 if let Some(ref location) = error.source_location {
807 let location_str =
808 format!("{}:{}:{}", location.file, location.line, location.column);
809 if !location_str.contains(pattern) {
810 return false;
811 }
812 } else {
813 return false;
814 }
815 }
816 MatchCriterion::StackContains(function) => {
817 if !error
818 .stack_trace
819 .iter()
820 .any(|frame| frame.function.contains(function))
821 {
822 return false;
823 }
824 }
825 MatchCriterion::Custom(matcher) => {
826 if !matcher(error) {
827 return false;
828 }
829 }
830 }
831 }
832 true
833 }
834
835 fn add_recovery_suggestions(&mut self, error: &mut DiagnosticError) {
837 if let Some(suggestions) = self.recovery_suggestions.get(&error.category) {
838 for suggestion in suggestions.iter().take(self.config.max_suggestions) {
839 error.suggestions.push(suggestion.clone());
840 self.stats.suggestions_provided += 1;
841 }
842 }
843 }
844
845 fn initialize_default_patterns(&mut self) {
847 let type_mismatch = ErrorPattern {
849 name: "type_mismatch".to_string(),
850 description: "Type mismatch in operation".to_string(),
851 criteria: vec![
852 MatchCriterion::MessageContains("type".to_string()),
853 MatchCriterion::CategoryEquals(ErrorCategory::TypeInference),
854 ],
855 common_causes: vec![
856 "Incorrect input types".to_string(),
857 "Missing type annotations".to_string(),
858 ],
859 solutions: vec![],
860 frequency: 0,
861 };
862
863 self.error_patterns
864 .insert("type_mismatch".to_string(), type_mismatch);
865
866 let shape_mismatch = ErrorPattern {
868 name: "shape_mismatch".to_string(),
869 description: "Shape mismatch in tensor operation".to_string(),
870 criteria: vec![
871 MatchCriterion::MessageContains("shape".to_string()),
872 MatchCriterion::CategoryEquals(ErrorCategory::ShapeInference),
873 ],
874 common_causes: vec![
875 "Incompatible tensor shapes".to_string(),
876 "Missing shape information".to_string(),
877 ],
878 solutions: vec![],
879 frequency: 0,
880 };
881
882 self.error_patterns
883 .insert("shape_mismatch".to_string(), shape_mismatch);
884 }
885
886 fn initialize_default_suggestions(&mut self) {
888 let type_suggestions = vec![RecoverySuggestion {
890 suggestion_type: SuggestionType::CodeChange,
891 message: "Check input types and add explicit type annotations".to_string(),
892 explanation: Some(
893 "Type inference failed. Consider adding explicit type information.".to_string(),
894 ),
895 code_example: Some("tensor.cast(DType::F32)".to_string()),
896 doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
897 confidence: 0.8,
898 auto_fix: None,
899 }];
900
901 self.recovery_suggestions
902 .insert(ErrorCategory::TypeInference, type_suggestions);
903
904 let shape_suggestions = vec![
906 RecoverySuggestion {
907 suggestion_type: SuggestionType::CodeChange,
908 message: "Verify tensor shapes are compatible for the operation".to_string(),
909 explanation: Some("Shape inference failed. Check that tensor dimensions match operation requirements.".to_string()),
910 code_example: Some("tensor.reshape(&[batch_size, channels, height, width])".to_string()),
911 doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
912 confidence: 0.9,
913 auto_fix: None,
914 },
915 ];
916
917 self.recovery_suggestions
918 .insert(ErrorCategory::ShapeInference, shape_suggestions);
919 }
920
921 pub fn push_context(&mut self, context: DiagnosticContext) {
923 self.context_stack.push(context);
924 }
925
926 pub fn pop_context(&mut self) -> Option<DiagnosticContext> {
928 self.context_stack.pop()
929 }
930
931 pub fn get_error_history(&self) -> &[DiagnosticError] {
933 &self.error_history
934 }
935
936 pub fn get_stats(&self) -> &DiagnosticsStats {
938 &self.stats
939 }
940
941 pub fn format_error(&self, error: &DiagnosticError, format_config: &FormatterConfig) -> String {
943 let formatter = ErrorFormatter::new(format_config.clone());
944 formatter.format(error)
945 }
946
947 pub fn get_similar_errors(&self, error: &DiagnosticError) -> Vec<&DiagnosticError> {
949 self.error_history
950 .iter()
951 .filter(|e| e.category == error.category && e.severity == error.severity)
952 .collect()
953 }
954
955 pub fn export_diagnostics(&self, output_path: &str) -> JitResult<()> {
957 let diagnostics_data = format!(
958 r#"{{"total_errors": {}, "errors_by_category": {:?}, "patterns": {}}}"#,
959 self.stats.total_errors,
960 self.stats.errors_by_category,
961 self.error_patterns.len()
962 );
963
964 std::fs::write(output_path, diagnostics_data)
965 .map_err(|e| JitError::RuntimeError(format!("Failed to export diagnostics: {}", e)))?;
966
967 Ok(())
968 }
969}
970
971impl ErrorFormatter {
972 pub fn new(config: FormatterConfig) -> Self {
974 Self { config }
975 }
976
977 pub fn format(&self, error: &DiagnosticError) -> String {
979 let mut output = String::new();
980
981 output.push_str(&format!(
983 "{}[{}] {}: {}\n",
984 self.color_for_severity(&error.severity),
985 error.severity.as_str(),
986 error.category.as_str(),
987 error.message
988 ));
989
990 if let Some(location) = &error.source_location {
992 output.push_str(&format!(
993 " --> {}:{}:{}\n",
994 location.file, location.line, location.column
995 ));
996
997 if self.config.include_source {
998 if let Some(snippet) = &location.snippet {
999 output.push_str(&format!(" |\n | {}\n |\n", snippet));
1000 }
1001 }
1002 }
1003
1004 output.push_str(&format!(
1006 " Context: {} ({})\n",
1007 error.context.operation, error.context.input
1008 ));
1009
1010 if self.config.include_stack_trace && !error.stack_trace.is_empty() {
1012 output.push_str(" Stack trace:\n");
1013 for frame in &error.stack_trace {
1014 output.push_str(&format!(
1015 " at {} ({}:{})\n",
1016 frame.function,
1017 frame.file.as_ref().unwrap_or(&"unknown".to_string()),
1018 frame.line.unwrap_or(0)
1019 ));
1020 }
1021 }
1022
1023 if self.config.include_suggestions && !error.suggestions.is_empty() {
1025 output.push_str(" Suggestions:\n");
1026 for suggestion in &error.suggestions {
1027 output.push_str(&format!(" - {}\n", suggestion.message));
1028 if let Some(explanation) = &suggestion.explanation {
1029 output.push_str(&format!(" {}\n", explanation));
1030 }
1031 }
1032 }
1033
1034 output.push_str(&self.reset_color());
1035 output
1036 }
1037
1038 fn color_for_severity(&self, severity: &ErrorSeverity) -> &str {
1040 if !self.config.use_colors {
1041 return "";
1042 }
1043
1044 match severity {
1045 ErrorSeverity::Info => "\x1b[36m", ErrorSeverity::Warning => "\x1b[33m", ErrorSeverity::Error => "\x1b[31m", ErrorSeverity::Fatal => "\x1b[35m", ErrorSeverity::Ice => "\x1b[41m", }
1051 }
1052
1053 fn reset_color(&self) -> &str {
1055 if self.config.use_colors {
1056 "\x1b[0m"
1057 } else {
1058 ""
1059 }
1060 }
1061}
1062
1063impl ErrorSeverity {
1064 pub fn as_str(&self) -> &str {
1066 match self {
1067 ErrorSeverity::Info => "INFO",
1068 ErrorSeverity::Warning => "WARNING",
1069 ErrorSeverity::Error => "ERROR",
1070 ErrorSeverity::Fatal => "FATAL",
1071 ErrorSeverity::Ice => "ICE",
1072 }
1073 }
1074}
1075
1076impl ErrorCategory {
1077 pub fn as_str(&self) -> &str {
1079 match self {
1080 ErrorCategory::GraphConstruction => "GRAPH",
1081 ErrorCategory::TypeInference => "TYPE",
1082 ErrorCategory::ShapeInference => "SHAPE",
1083 ErrorCategory::Optimization => "OPT",
1084 ErrorCategory::CodeGeneration => "CODEGEN",
1085 ErrorCategory::Runtime => "RUNTIME",
1086 ErrorCategory::Memory => "MEMORY",
1087 ErrorCategory::Resource => "RESOURCE",
1088 ErrorCategory::UserInput => "INPUT",
1089 ErrorCategory::Internal => "INTERNAL",
1090 ErrorCategory::External => "EXTERNAL",
1091 }
1092 }
1093}
1094
1095impl fmt::Display for DiagnosticError {
1096 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1097 write!(
1098 f,
1099 "[{}] {}: {}",
1100 self.severity.as_str(),
1101 self.category.as_str(),
1102 self.message
1103 )
1104 }
1105}
1106
1107#[cfg(test)]
1108mod tests {
1109 use super::*;
1110
1111 #[test]
1112 fn test_diagnostics_manager_creation() {
1113 let manager = ErrorDiagnosticsManager::with_defaults();
1114 assert!(manager.config.enabled);
1115 assert_eq!(manager.config.max_history_size, 1000);
1116 }
1117
1118 #[test]
1119 fn test_error_recording() {
1120 let mut manager = ErrorDiagnosticsManager::with_defaults();
1121 let error = JitError::RuntimeError("Test error".to_string());
1122
1123 let diagnostic_error = manager.record_error(error);
1124 assert_eq!(diagnostic_error.category, ErrorCategory::Runtime);
1125 assert_eq!(diagnostic_error.severity, ErrorSeverity::Error);
1126 assert!(diagnostic_error.message.contains("Test error"));
1127 }
1128
1129 #[test]
1130 fn test_error_categorization() {
1131 let manager = ErrorDiagnosticsManager::with_defaults();
1132
1133 let graph_error = JitError::GraphError("Graph error".to_string());
1134 assert_eq!(
1135 manager.categorize_error(&graph_error),
1136 ErrorCategory::GraphConstruction
1137 );
1138
1139 let runtime_error = JitError::RuntimeError("Runtime error".to_string());
1140 assert_eq!(
1141 manager.categorize_error(&runtime_error),
1142 ErrorCategory::Runtime
1143 );
1144 }
1145
1146 #[test]
1147 fn test_error_formatting() {
1148 let mut manager = ErrorDiagnosticsManager::with_defaults();
1149 let error = JitError::RuntimeError("Test error".to_string());
1150 let diagnostic_error = manager.record_error(error);
1151
1152 let formatter_config = FormatterConfig::default();
1153 let formatted = manager.format_error(&diagnostic_error, &formatter_config);
1154
1155 assert!(formatted.contains("ERROR"));
1156 assert!(formatted.contains("RUNTIME"));
1157 assert!(formatted.contains("Test error"));
1158 }
1159
1160 #[test]
1161 fn test_context_stack() {
1162 let mut manager = ErrorDiagnosticsManager::with_defaults();
1163
1164 let context = DiagnosticContext {
1165 operation: "test_operation".to_string(),
1166 input: "test_input".to_string(),
1167 expected: None,
1168 actual: None,
1169 environment: manager.get_environment_info(),
1170 data: HashMap::new(),
1171 };
1172
1173 manager.push_context(context.clone());
1174 assert_eq!(manager.context_stack.len(), 1);
1175
1176 let popped = manager.pop_context().unwrap();
1177 assert_eq!(popped.operation, "test_operation");
1178 assert_eq!(manager.context_stack.len(), 0);
1179 }
1180}