1use crate::error::{Result, SklearsComposeError};
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, VecDeque};
14use std::sync::{Arc, RwLock};
15use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
16
17#[derive(Debug)]
19pub struct ErrorMessageEnhancer {
20 pattern_analyzer: Arc<RwLock<ErrorPatternAnalyzer>>,
22
23 context_collector: Arc<RwLock<ErrorContextCollector>>,
25
26 suggestion_engine: Arc<RwLock<SuggestionEngine>>,
28
29 recovery_advisor: Arc<RwLock<RecoveryAdvisor>>,
31
32 error_formatter: Arc<RwLock<ErrorFormatter>>,
34
35 config: ErrorEnhancementConfig,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ErrorEnhancementConfig {
42 pub enable_pattern_analysis: bool,
44
45 pub enable_context_collection: bool,
47
48 pub enable_auto_suggestions: bool,
50
51 pub enable_recovery_strategies: bool,
53
54 pub max_suggestions_per_error: usize,
56
57 pub suggestion_confidence_threshold: f64,
59
60 pub enable_learning: bool,
62
63 pub error_history_size: usize,
65
66 pub enable_detailed_diagnostics: bool,
68}
69
70#[derive(Debug)]
72pub struct ErrorPatternAnalyzer {
73 error_patterns: HashMap<String, ErrorPattern>,
75
76 error_frequency: HashMap<String, usize>,
78
79 resolution_success: HashMap<String, f64>,
81
82 config: PatternAnalysisConfig,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct ErrorPattern {
89 pub pattern_id: String,
91
92 pub error_type: String,
94
95 pub message_patterns: Vec<String>,
97
98 pub context_patterns: Vec<ContextPattern>,
100
101 pub resolution_strategies: Vec<ResolutionStrategy>,
103
104 pub frequency: usize,
106
107 pub last_occurrence: SystemTime,
109
110 pub success_rate: f64,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct ContextPattern {
117 pub context_type: ContextType,
119
120 pub pattern: String,
122
123 pub confidence: f64,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
129pub enum ContextType {
130 DataShape,
132 DataType,
134 Configuration,
136 Environment,
138 Dependencies,
140 Resources,
142 Pipeline,
144 Model,
146 Performance,
148 Network,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct ResolutionStrategy {
155 pub strategy_id: String,
157
158 pub description: String,
160
161 pub steps: Vec<ResolutionStep>,
163
164 pub success_rate: f64,
166
167 pub difficulty: DifficultyLevel,
169
170 pub expertise_level: ExpertiseLevel,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct ResolutionStep {
177 pub description: String,
179
180 pub code_example: Option<String>,
182
183 pub documentation_link: Option<String>,
185
186 pub validation: Option<String>,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub enum DifficultyLevel {
193 Trivial,
195 Easy,
197 Medium,
199 Hard,
201 Expert,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207pub enum ExpertiseLevel {
208 Beginner,
210 Intermediate,
212 Advanced,
214 Expert,
216}
217
218#[derive(Debug)]
220pub struct ErrorContextCollector {
221 context_providers: HashMap<ContextType, Box<dyn ContextProvider>>,
223
224 context_cache: HashMap<String, EnhancedErrorContext>,
226
227 config: ContextCollectionConfig,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct EnhancedErrorContext {
234 pub timestamp: SystemTime,
236
237 pub pipeline_context: PipelineContext,
239
240 pub data_context: DataContext,
242
243 pub environment_context: EnvironmentContext,
245
246 pub performance_context: PerformanceContext,
248
249 pub configuration_context: ConfigurationContext,
251
252 pub call_stack: Vec<StackFrame>,
254
255 pub related_issues: Vec<RelatedIssue>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct PipelineContext {
262 pub pipeline_name: String,
264
265 pub current_step: String,
267
268 pub step_index: usize,
270
271 pub total_steps: usize,
273
274 pub completed_steps: Vec<String>,
276
277 pub pipeline_config: HashMap<String, String>,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct DataContext {
284 pub input_shape: Vec<usize>,
286
287 pub input_dtype: String,
289
290 pub expected_shape: Option<Vec<usize>>,
292
293 pub expected_dtype: Option<String>,
295
296 pub data_statistics: DataStatistics,
298
299 pub missing_values: MissingValueInfo,
301
302 pub quality_metrics: DataQualityMetrics,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct MissingValueInfo {
309 pub total_missing: usize,
311
312 pub missing_per_feature: Vec<usize>,
314
315 pub missing_patterns: Vec<String>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct DataQualityMetrics {
322 pub quality_score: f64,
324
325 pub completeness: f64,
327
328 pub consistency: f64,
330
331 pub validity: f64,
333
334 pub quality_issues: Vec<QualityIssue>,
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct QualityIssue {
341 pub issue_type: String,
343
344 pub description: String,
346
347 pub severity: SeverityLevel,
349
350 pub affected_elements: Vec<String>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub enum SeverityLevel {
357 Low,
359 Medium,
361 High,
363 Critical,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct EnvironmentContext {
370 pub os_info: String,
372
373 pub available_memory: u64,
375
376 pub cpu_info: String,
378
379 pub runtime_version: String,
381
382 pub package_versions: HashMap<String, String>,
384
385 pub environment_variables: HashMap<String, String>,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct PerformanceContext {
392 pub memory_usage: u64,
394
395 pub cpu_utilization: f64,
397
398 pub execution_time: Duration,
400
401 pub bottlenecks: Vec<PerformanceBottleneck>,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
407pub struct PerformanceBottleneck {
408 pub location: String,
410
411 pub bottleneck_type: String,
413
414 pub impact: f64,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, Default)]
420pub struct ConfigurationContext {
421 pub model_config: HashMap<String, String>,
423
424 pub pipeline_config: HashMap<String, String>,
426
427 pub training_config: HashMap<String, String>,
429
430 pub validation_results: Vec<ConfigValidationResult>,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct ConfigValidationResult {
437 pub config_key: String,
439
440 pub is_valid: bool,
442
443 pub message: String,
445
446 pub suggested_value: Option<String>,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct StackFrame {
453 pub function_name: String,
455
456 pub file_name: String,
458
459 pub line_number: usize,
461
462 pub module_name: String,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct RelatedIssue {
469 pub issue_type: String,
471
472 pub message: String,
474
475 pub relationship: IssueRelationship,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize)]
481pub enum IssueRelationship {
482 CausedBy,
484 LeadsTo,
486 RelatedTo,
488 SimilarTo,
490}
491
492#[derive(Debug)]
494pub struct SuggestionEngine {
495 generators: HashMap<String, Box<dyn SuggestionGenerator>>,
497
498 learning_model: Option<SuggestionRankingModel>,
500
501 suggestion_cache: HashMap<String, Vec<ActionableSuggestion>>,
503
504 config: SuggestionEngineConfig,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct ActionableSuggestion {
511 pub suggestion_id: String,
513
514 pub title: String,
516
517 pub description: String,
519
520 pub implementation_steps: Vec<ImplementationStep>,
522
523 pub code_examples: Vec<CodeExample>,
525
526 pub confidence: f64,
528
529 pub estimated_time: Duration,
531
532 pub success_probability: f64,
534
535 pub expertise_level: ExpertiseLevel,
537
538 pub prerequisites: Vec<String>,
540
541 pub validation_method: Option<String>,
543
544 pub follow_up_suggestions: Vec<String>,
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct ImplementationStep {
551 pub step_number: usize,
553
554 pub description: String,
556
557 pub code_snippet: Option<String>,
559
560 pub command: Option<String>,
562
563 pub expected_outcome: String,
565
566 pub validation_check: Option<String>,
568}
569
570#[derive(Debug, Clone, Serialize, Deserialize)]
572pub struct CodeExample {
573 pub language: String,
575
576 pub code: String,
578
579 pub description: String,
581
582 pub file_path: Option<String>,
584}
585
586#[derive(Debug)]
588pub struct RecoveryAdvisor {
589 recovery_strategies: HashMap<String, Vec<RecoveryStrategy>>,
591
592 auto_recovery: HashMap<String, Box<dyn AutoRecoveryHandler>>,
594
595 recovery_history: VecDeque<RecoveryAttempt>,
597
598 config: RecoveryConfig,
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct RecoveryStrategy {
605 pub strategy_id: String,
607
608 pub name: String,
610
611 pub description: String,
613
614 pub actions: Vec<RecoveryAction>,
616
617 pub is_automatic: bool,
619
620 pub success_rate: f64,
622
623 pub risk_level: RiskLevel,
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct RecoveryAction {
630 pub action_type: RecoveryActionType,
632
633 pub description: String,
635
636 pub parameters: HashMap<String, String>,
638
639 pub rollback_action: Option<String>,
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize)]
645pub enum RecoveryActionType {
646 DataTransformation,
648 ConfigurationAdjustment,
650 ModelReset,
652 ResourceReallocation,
654 ParameterTuning,
656 FallbackStrategy,
658 Manual,
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize)]
664pub enum RiskLevel {
665 Safe,
667 Low,
669 Medium,
671 High,
673 Dangerous,
675}
676
677#[derive(Debug, Clone)]
679pub struct RecoveryAttempt {
680 pub error_id: String,
682
683 pub strategy_id: String,
685
686 pub actions_taken: Vec<String>,
688
689 pub outcome: RecoveryOutcome,
691
692 pub recovery_time: Duration,
694
695 pub timestamp: SystemTime,
697}
698
699#[derive(Debug, Clone)]
701pub enum RecoveryOutcome {
702 Success,
704 PartialSuccess,
706 Failure,
708 Manual,
710}
711
712#[derive(Debug)]
714pub struct ErrorFormatter {
715 format_templates: HashMap<String, ErrorTemplate>,
717
718 localization: HashMap<String, HashMap<String, String>>,
720
721 config: FormatterConfig,
723}
724
725#[derive(Debug, Clone)]
727pub struct ErrorTemplate {
728 pub template_id: String,
730
731 pub message_template: String,
733
734 pub context_sections: Vec<ContextSection>,
736
737 pub suggestion_format: SuggestionFormat,
739
740 pub output_format: OutputFormat,
742}
743
744#[derive(Debug, Clone)]
746pub struct ContextSection {
747 pub title: String,
749
750 pub content_template: String,
752
753 pub optional: bool,
755}
756
757#[derive(Debug, Clone)]
759pub struct SuggestionFormat {
760 pub max_suggestions: usize,
762
763 pub include_code_examples: bool,
765
766 pub include_confidence_scores: bool,
768
769 pub include_time_estimates: bool,
771}
772
773#[derive(Debug, Clone)]
775pub enum OutputFormat {
776 PlainText,
778 Markdown,
780 Html,
782 Json,
784 Yaml,
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize)]
790pub struct EnhancedErrorMessage {
791 pub original_error: String,
793
794 pub enhanced_message: String,
796
797 pub error_classification: ErrorClassification,
799
800 pub diagnostics: EnhancedErrorContext,
802
803 pub suggestions: Vec<ActionableSuggestion>,
805
806 pub recovery_strategies: Vec<RecoveryStrategy>,
808
809 pub documentation_links: Vec<DocumentationLink>,
811
812 pub similar_issues: Vec<SimilarIssue>,
814}
815
816#[derive(Debug, Clone, Serialize, Deserialize)]
818pub struct ErrorClassification {
819 pub category: ErrorCategory,
821
822 pub severity: SeverityLevel,
824
825 pub frequency: ErrorFrequency,
827
828 pub resolution_difficulty: DifficultyLevel,
830}
831
832#[derive(Debug, Clone, Serialize, Deserialize)]
834pub enum ErrorCategory {
835 DataError,
837 ConfigurationError,
839 EnvironmentError,
841 ModelError,
843 PipelineError,
845 PerformanceError,
847 NetworkError,
849 SecurityError,
851 Unknown,
853}
854
855#[derive(Debug, Clone, Serialize, Deserialize)]
857pub enum ErrorFrequency {
858 Rare,
860 Occasional,
862 Common,
864 Frequent,
866 Constant,
868}
869
870#[derive(Debug, Clone, Serialize, Deserialize)]
872pub struct DocumentationLink {
873 pub title: String,
875
876 pub url: String,
878
879 pub description: String,
881
882 pub relevance: f64,
884}
885
886#[derive(Debug, Clone, Serialize, Deserialize)]
888pub struct SimilarIssue {
889 pub issue_id: String,
891
892 pub description: String,
894
895 pub similarity: f64,
897
898 pub resolution: Option<String>,
900}
901
902#[derive(Debug, Clone, Serialize, Deserialize)]
904pub struct DataStatistics {
905 pub n_samples: usize,
907
908 pub n_features: usize,
910
911 pub means: Vec<f64>,
913
914 pub stds: Vec<f64>,
916
917 pub mins: Vec<f64>,
919
920 pub maxs: Vec<f64>,
922}
923
924#[derive(Debug, Clone)]
926pub struct PatternAnalysisConfig {
927 pub max_patterns: usize,
928 pub pattern_similarity_threshold: f64,
929 pub learning_rate: f64,
930}
931
932#[derive(Debug, Clone)]
933pub struct ContextCollectionConfig {
934 pub enable_detailed_context: bool,
935 pub max_context_size: usize,
936 pub context_timeout: Duration,
937}
938
939#[derive(Debug, Clone)]
940pub struct SuggestionEngineConfig {
941 pub max_suggestions: usize,
942 pub confidence_threshold: f64,
943 pub enable_machine_learning: bool,
944}
945
946#[derive(Debug, Clone)]
947pub struct RecoveryConfig {
948 pub enable_auto_recovery: bool,
949 pub max_recovery_attempts: usize,
950 pub recovery_timeout: Duration,
951}
952
953#[derive(Debug, Clone)]
954pub struct FormatterConfig {
955 pub default_language: String,
956 pub include_technical_details: bool,
957 pub max_message_length: usize,
958}
959
960pub trait ContextProvider: std::fmt::Debug + Send + Sync {
962 fn collect_context(&self, error: &SklearsComposeError) -> Result<HashMap<String, String>>;
963 fn context_type(&self) -> ContextType;
964}
965
966pub trait SuggestionGenerator: std::fmt::Debug + Send + Sync {
968 fn generate_suggestions(
969 &self,
970 error: &SklearsComposeError,
971 context: &EnhancedErrorContext,
972 ) -> Result<Vec<ActionableSuggestion>>;
973 fn error_types(&self) -> Vec<String>;
974}
975
976pub trait AutoRecoveryHandler: std::fmt::Debug + Send + Sync {
978 fn can_recover(&self, error: &SklearsComposeError) -> bool;
979 fn attempt_recovery(
980 &self,
981 error: &SklearsComposeError,
982 context: &EnhancedErrorContext,
983 ) -> Result<RecoveryOutcome>;
984}
985
986#[derive(Debug)]
988pub struct SuggestionRankingModel {
989 feature_weights: HashMap<String, f64>,
991
992 success_rates: HashMap<String, f64>,
994
995 parameters: ModelParameters,
997}
998
999#[derive(Debug, Clone)]
1000pub struct ModelParameters {
1001 pub learning_rate: f64,
1002 pub regularization: f64,
1003 pub feature_count: usize,
1004}
1005
1006impl Default for ErrorEnhancementConfig {
1007 fn default() -> Self {
1008 Self {
1009 enable_pattern_analysis: true,
1010 enable_context_collection: true,
1011 enable_auto_suggestions: true,
1012 enable_recovery_strategies: true,
1013 max_suggestions_per_error: 5,
1014 suggestion_confidence_threshold: 0.7,
1015 enable_learning: true,
1016 error_history_size: 1000,
1017 enable_detailed_diagnostics: true,
1018 }
1019 }
1020}
1021
1022impl Default for ErrorMessageEnhancer {
1023 fn default() -> Self {
1024 Self::new()
1025 }
1026}
1027
1028impl ErrorMessageEnhancer {
1029 #[must_use]
1031 pub fn new() -> Self {
1032 Self::with_config(ErrorEnhancementConfig::default())
1033 }
1034
1035 #[must_use]
1037 pub fn with_config(config: ErrorEnhancementConfig) -> Self {
1038 let pattern_config = PatternAnalysisConfig {
1039 max_patterns: 500,
1040 pattern_similarity_threshold: 0.8,
1041 learning_rate: 0.01,
1042 };
1043
1044 let context_config = ContextCollectionConfig {
1045 enable_detailed_context: config.enable_detailed_diagnostics,
1046 max_context_size: 10_000,
1047 context_timeout: Duration::from_secs(5),
1048 };
1049
1050 let suggestion_config = SuggestionEngineConfig {
1051 max_suggestions: config.max_suggestions_per_error,
1052 confidence_threshold: config.suggestion_confidence_threshold,
1053 enable_machine_learning: config.enable_learning,
1054 };
1055
1056 let recovery_config = RecoveryConfig {
1057 enable_auto_recovery: config.enable_recovery_strategies,
1058 max_recovery_attempts: 3,
1059 recovery_timeout: Duration::from_secs(30),
1060 };
1061
1062 let formatter_config = FormatterConfig {
1063 default_language: "en".to_string(),
1064 include_technical_details: config.enable_detailed_diagnostics,
1065 max_message_length: 5000,
1066 };
1067
1068 Self {
1069 pattern_analyzer: Arc::new(RwLock::new(ErrorPatternAnalyzer::new(pattern_config))),
1070 context_collector: Arc::new(RwLock::new(ErrorContextCollector::new(context_config))),
1071 suggestion_engine: Arc::new(RwLock::new(SuggestionEngine::new(suggestion_config))),
1072 recovery_advisor: Arc::new(RwLock::new(RecoveryAdvisor::new(recovery_config))),
1073 error_formatter: Arc::new(RwLock::new(ErrorFormatter::new(formatter_config))),
1074 config,
1075 }
1076 }
1077
1078 pub fn enhance_error(&self, error: &SklearsComposeError) -> Result<EnhancedErrorMessage> {
1080 let context = if self.config.enable_context_collection {
1082 self.context_collector
1083 .write()
1084 .unwrap()
1085 .collect_context(error)?
1086 } else {
1087 EnhancedErrorContext::default()
1088 };
1089
1090 let classification = if self.config.enable_pattern_analysis {
1092 self.pattern_analyzer
1093 .write()
1094 .unwrap()
1095 .analyze_error(error, &context)?
1096 } else {
1097 ErrorClassification::default()
1098 };
1099
1100 let suggestions = if self.config.enable_auto_suggestions {
1102 self.suggestion_engine
1103 .read()
1104 .unwrap()
1105 .generate_suggestions(error, &context)?
1106 } else {
1107 Vec::new()
1108 };
1109
1110 let recovery_strategies = if self.config.enable_recovery_strategies {
1112 self.recovery_advisor
1113 .read()
1114 .unwrap()
1115 .generate_recovery_strategies(error, &context)?
1116 } else {
1117 Vec::new()
1118 };
1119
1120 let enhanced_message =
1122 self.error_formatter
1123 .read()
1124 .unwrap()
1125 .format_error(error, &context, &suggestions)?;
1126
1127 let documentation_links = self.generate_documentation_links(error, &classification)?;
1129
1130 let similar_issues = self.find_similar_issues(error, &context)?;
1132
1133 Ok(EnhancedErrorMessage {
1134 original_error: error.to_string(),
1135 enhanced_message,
1136 error_classification: classification,
1137 diagnostics: context,
1138 suggestions,
1139 recovery_strategies,
1140 documentation_links,
1141 similar_issues,
1142 })
1143 }
1144
1145 pub fn attempt_recovery(&self, error: &SklearsComposeError) -> Result<RecoveryOutcome> {
1147 let context = self
1148 .context_collector
1149 .write()
1150 .unwrap()
1151 .collect_context(error)?;
1152 self.recovery_advisor
1153 .write()
1154 .unwrap()
1155 .attempt_auto_recovery(error, &context)
1156 }
1157
1158 pub fn learn_from_resolution(
1160 &self,
1161 error: &SklearsComposeError,
1162 suggestion_id: &str,
1163 outcome: bool,
1164 ) -> Result<()> {
1165 if self.config.enable_learning {
1166 self.pattern_analyzer
1167 .write()
1168 .unwrap()
1169 .update_success_rate(suggestion_id, outcome)?;
1170 self.suggestion_engine
1171 .write()
1172 .unwrap()
1173 .update_ranking_model(suggestion_id, outcome)?;
1174 }
1175 Ok(())
1176 }
1177
1178 pub fn export_statistics(&self) -> Result<ErrorEnhancementStatistics> {
1180 let pattern_stats = self.pattern_analyzer.read().unwrap().get_statistics();
1181 let suggestion_stats = self.suggestion_engine.read().unwrap().get_statistics();
1182 let recovery_stats = self.recovery_advisor.read().unwrap().get_statistics();
1183
1184 Ok(ErrorEnhancementStatistics {
1185 total_errors_analyzed: pattern_stats.total_patterns,
1186 suggestions_generated: suggestion_stats.total_suggestions,
1187 recovery_attempts: recovery_stats.total_attempts,
1188 success_rate: recovery_stats.success_rate,
1189 pattern_accuracy: pattern_stats.accuracy,
1190 suggestion_confidence: suggestion_stats.average_confidence,
1191 })
1192 }
1193
1194 fn generate_documentation_links(
1196 &self,
1197 error: &SklearsComposeError,
1198 classification: &ErrorClassification,
1199 ) -> Result<Vec<DocumentationLink>> {
1200 let mut links = Vec::new();
1201
1202 match classification.category {
1204 ErrorCategory::DataError => {
1205 links.push(DocumentationLink {
1206 title: "Data Input Guidelines".to_string(),
1207 url: "https://docs.rs/sklears-compose/data-input".to_string(),
1208 description: "Guide for proper data formatting and validation".to_string(),
1209 relevance: 0.9,
1210 });
1211 }
1212 ErrorCategory::ConfigurationError => {
1213 links.push(DocumentationLink {
1214 title: "Configuration Reference".to_string(),
1215 url: "https://docs.rs/sklears-compose/configuration".to_string(),
1216 description: "Complete reference for configuration options".to_string(),
1217 relevance: 0.95,
1218 });
1219 }
1220 _ => {}
1221 }
1222
1223 Ok(links)
1224 }
1225
1226 fn find_similar_issues(
1227 &self,
1228 error: &SklearsComposeError,
1229 context: &EnhancedErrorContext,
1230 ) -> Result<Vec<SimilarIssue>> {
1231 Ok(Vec::new())
1234 }
1235}
1236
1237#[derive(Debug, Clone, Serialize, Deserialize)]
1239pub struct ErrorEnhancementStatistics {
1240 pub total_errors_analyzed: usize,
1241 pub suggestions_generated: usize,
1242 pub recovery_attempts: usize,
1243 pub success_rate: f64,
1244 pub pattern_accuracy: f64,
1245 pub suggestion_confidence: f64,
1246}
1247
1248impl ErrorPatternAnalyzer {
1251 fn new(config: PatternAnalysisConfig) -> Self {
1252 Self {
1253 error_patterns: HashMap::new(),
1254 error_frequency: HashMap::new(),
1255 resolution_success: HashMap::new(),
1256 config,
1257 }
1258 }
1259
1260 fn analyze_error(
1261 &mut self,
1262 error: &SklearsComposeError,
1263 context: &EnhancedErrorContext,
1264 ) -> Result<ErrorClassification> {
1265 let error_type = format!("{error:?}");
1266
1267 *self.error_frequency.entry(error_type.clone()).or_insert(0) += 1;
1269
1270 let category = self.classify_error_category(error);
1272 let severity = self.assess_severity(error, context);
1273 let frequency = self.assess_frequency(&error_type);
1274 let resolution_difficulty = self.assess_resolution_difficulty(error);
1275
1276 Ok(ErrorClassification {
1277 category,
1278 severity,
1279 frequency,
1280 resolution_difficulty,
1281 })
1282 }
1283
1284 fn classify_error_category(&self, error: &SklearsComposeError) -> ErrorCategory {
1285 match error {
1286 SklearsComposeError::InvalidData { .. } => ErrorCategory::DataError,
1287 SklearsComposeError::InvalidConfiguration(_) => ErrorCategory::ConfigurationError,
1288 SklearsComposeError::InvalidOperation(_) => ErrorCategory::PipelineError,
1289 SklearsComposeError::Serialization(_) | SklearsComposeError::Io(_) => {
1290 ErrorCategory::EnvironmentError
1291 }
1292 SklearsComposeError::Core(_) => ErrorCategory::ModelError,
1293 SklearsComposeError::Other(reason) => {
1294 let lower = reason.to_lowercase();
1295 if lower.contains("shape") || lower.contains("dimension") {
1296 ErrorCategory::DataError
1297 } else if lower.contains("config") || lower.contains("parameter") {
1298 ErrorCategory::ConfigurationError
1299 } else if lower.contains("memory") || lower.contains("resource") {
1300 ErrorCategory::EnvironmentError
1301 } else if lower.contains("model") || lower.contains("fit") {
1302 ErrorCategory::ModelError
1303 } else if lower.contains("pipeline") {
1304 ErrorCategory::PipelineError
1305 } else {
1306 ErrorCategory::Unknown
1307 }
1308 }
1309 }
1310 }
1311
1312 fn assess_severity(
1313 &self,
1314 error: &SklearsComposeError,
1315 context: &EnhancedErrorContext,
1316 ) -> SeverityLevel {
1317 if context.performance_context.memory_usage > 1_000_000_000 {
1319 SeverityLevel::High
1320 } else {
1321 SeverityLevel::Medium
1322 }
1323 }
1324
1325 fn assess_frequency(&self, error_type: &str) -> ErrorFrequency {
1326 let count = self.error_frequency.get(error_type).unwrap_or(&0);
1327 match count {
1328 0..=1 => ErrorFrequency::Rare,
1329 2..=5 => ErrorFrequency::Occasional,
1330 6..=15 => ErrorFrequency::Common,
1331 16..=50 => ErrorFrequency::Frequent,
1332 _ => ErrorFrequency::Constant,
1333 }
1334 }
1335
1336 fn assess_resolution_difficulty(&self, error: &SklearsComposeError) -> DifficultyLevel {
1337 let error_str = error.to_string().to_lowercase();
1339
1340 if error_str.contains("simple") || error_str.contains("basic") {
1341 DifficultyLevel::Easy
1342 } else if error_str.contains("complex") || error_str.contains("advanced") {
1343 DifficultyLevel::Hard
1344 } else {
1345 DifficultyLevel::Medium
1346 }
1347 }
1348
1349 fn update_success_rate(&mut self, suggestion_id: &str, success: bool) -> Result<()> {
1350 let current_rate = self.resolution_success.get(suggestion_id).unwrap_or(&0.5);
1351 let new_rate = if success {
1352 (current_rate + 0.1).min(1.0)
1353 } else {
1354 (current_rate - 0.1).max(0.0)
1355 };
1356 self.resolution_success
1357 .insert(suggestion_id.to_string(), new_rate);
1358 Ok(())
1359 }
1360
1361 fn get_statistics(&self) -> PatternStatistics {
1362 PatternStatistics {
1364 total_patterns: self.error_patterns.len(),
1365 accuracy: 0.85, }
1367 }
1368}
1369
1370#[derive(Debug)]
1371struct PatternStatistics {
1372 total_patterns: usize,
1373 accuracy: f64,
1374}
1375
1376impl ErrorContextCollector {
1377 fn new(config: ContextCollectionConfig) -> Self {
1378 let mut context_providers: HashMap<ContextType, Box<dyn ContextProvider>> = HashMap::new();
1379
1380 context_providers.insert(
1382 ContextType::Environment,
1383 Box::new(EnvironmentContextProvider::new()),
1384 );
1385 context_providers.insert(
1386 ContextType::Performance,
1387 Box::new(PerformanceContextProvider::new()),
1388 );
1389
1390 Self {
1391 context_providers,
1392 context_cache: HashMap::new(),
1393 config,
1394 }
1395 }
1396
1397 fn collect_context(&mut self, error: &SklearsComposeError) -> Result<EnhancedErrorContext> {
1398 let context = EnhancedErrorContext {
1400 timestamp: SystemTime::now(),
1401 pipeline_context: PipelineContext::default(),
1402 data_context: DataContext::default(),
1403 environment_context: EnvironmentContext::default(),
1404 performance_context: PerformanceContext::default(),
1405 configuration_context: ConfigurationContext::default(),
1406 call_stack: Vec::new(),
1407 related_issues: Vec::new(),
1408 };
1409
1410 for (context_type, provider) in &self.context_providers {
1412 if let Ok(additional_context) = provider.collect_context(error) {
1413 }
1415 }
1416
1417 Ok(context)
1418 }
1419}
1420
1421impl SuggestionEngine {
1422 fn new(config: SuggestionEngineConfig) -> Self {
1423 let mut generators: HashMap<String, Box<dyn SuggestionGenerator>> = HashMap::new();
1424
1425 generators.insert(
1427 "DataError".to_string(),
1428 Box::new(DataErrorSuggestionGenerator::new()),
1429 );
1430 generators.insert(
1431 "ConfigurationError".to_string(),
1432 Box::new(ConfigurationErrorSuggestionGenerator::new()),
1433 );
1434
1435 Self {
1436 generators,
1437 learning_model: None,
1438 suggestion_cache: HashMap::new(),
1439 config,
1440 }
1441 }
1442
1443 fn generate_suggestions(
1444 &self,
1445 error: &SklearsComposeError,
1446 context: &EnhancedErrorContext,
1447 ) -> Result<Vec<ActionableSuggestion>> {
1448 let error_type = format!("{error:?}");
1449
1450 let mut suggestions = Vec::new();
1451
1452 for (generator_type, generator) in &self.generators {
1454 if error_type.contains(generator_type) {
1455 if let Ok(mut generated) = generator.generate_suggestions(error, context) {
1456 suggestions.append(&mut generated);
1457 }
1458 }
1459 }
1460
1461 suggestions.retain(|s| s.confidence >= self.config.confidence_threshold);
1463
1464 suggestions.truncate(self.config.max_suggestions);
1466
1467 Ok(suggestions)
1468 }
1469
1470 fn update_ranking_model(&mut self, suggestion_id: &str, success: bool) -> Result<()> {
1471 Ok(())
1474 }
1475
1476 fn get_statistics(&self) -> SuggestionStatistics {
1477 SuggestionStatistics {
1479 total_suggestions: self.suggestion_cache.len(),
1480 average_confidence: 0.8, }
1482 }
1483}
1484
1485#[derive(Debug)]
1486struct SuggestionStatistics {
1487 total_suggestions: usize,
1488 average_confidence: f64,
1489}
1490
1491impl RecoveryAdvisor {
1492 fn new(config: RecoveryConfig) -> Self {
1493 Self {
1494 recovery_strategies: HashMap::new(),
1495 auto_recovery: HashMap::new(),
1496 recovery_history: VecDeque::with_capacity(1000),
1497 config,
1498 }
1499 }
1500
1501 fn generate_recovery_strategies(
1502 &self,
1503 error: &SklearsComposeError,
1504 context: &EnhancedErrorContext,
1505 ) -> Result<Vec<RecoveryStrategy>> {
1506 let error_type = format!("{error:?}");
1507
1508 if let Some(strategies) = self.recovery_strategies.get(&error_type) {
1509 Ok(strategies.clone())
1510 } else {
1511 Ok(vec![RecoveryStrategy {
1513 strategy_id: "manual_review".to_string(),
1514 name: "Manual Review".to_string(),
1515 description: "Manually review the error and fix the underlying issue".to_string(),
1516 actions: vec![RecoveryAction {
1517 action_type: RecoveryActionType::Manual,
1518 description: "Review error message and fix the issue".to_string(),
1519 parameters: HashMap::new(),
1520 rollback_action: None,
1521 }],
1522 is_automatic: false,
1523 success_rate: 0.9,
1524 risk_level: RiskLevel::Safe,
1525 }])
1526 }
1527 }
1528
1529 fn attempt_auto_recovery(
1530 &mut self,
1531 error: &SklearsComposeError,
1532 context: &EnhancedErrorContext,
1533 ) -> Result<RecoveryOutcome> {
1534 if !self.config.enable_auto_recovery {
1535 return Ok(RecoveryOutcome::Manual);
1536 }
1537
1538 let error_type = format!("{error:?}");
1539
1540 if let Some(handler) = self.auto_recovery.get(&error_type) {
1541 if handler.can_recover(error) {
1542 let start_time = Instant::now();
1543 let outcome = handler.attempt_recovery(error, context)?;
1544
1545 let attempt = RecoveryAttempt {
1547 error_id: format!(
1548 "err_{}",
1549 SystemTime::now()
1550 .duration_since(UNIX_EPOCH)
1551 .unwrap()
1552 .as_millis()
1553 ),
1554 strategy_id: "auto_recovery".to_string(),
1555 actions_taken: vec!["automatic_recovery".to_string()],
1556 outcome: outcome.clone(),
1557 recovery_time: start_time.elapsed(),
1558 timestamp: SystemTime::now(),
1559 };
1560
1561 self.recovery_history.push_back(attempt);
1562 if self.recovery_history.len() > 1000 {
1563 self.recovery_history.pop_front();
1564 }
1565
1566 return Ok(outcome);
1567 }
1568 }
1569
1570 Ok(RecoveryOutcome::Manual)
1571 }
1572
1573 fn get_statistics(&self) -> RecoveryStatistics {
1574 let total_attempts = self.recovery_history.len();
1575 let successful_attempts = self
1576 .recovery_history
1577 .iter()
1578 .filter(|attempt| matches!(attempt.outcome, RecoveryOutcome::Success))
1579 .count();
1580
1581 let success_rate = if total_attempts > 0 {
1582 successful_attempts as f64 / total_attempts as f64
1583 } else {
1584 0.0
1585 };
1586
1587 RecoveryStatistics {
1589 total_attempts,
1590 success_rate,
1591 }
1592 }
1593}
1594
1595#[derive(Debug)]
1596struct RecoveryStatistics {
1597 total_attempts: usize,
1598 success_rate: f64,
1599}
1600
1601impl ErrorFormatter {
1602 fn new(config: FormatterConfig) -> Self {
1603 let mut format_templates = HashMap::new();
1604
1605 format_templates.insert("default".to_string(), ErrorTemplate::default());
1607
1608 Self {
1609 format_templates,
1610 localization: HashMap::new(),
1611 config,
1612 }
1613 }
1614
1615 fn format_error(
1616 &self,
1617 error: &SklearsComposeError,
1618 context: &EnhancedErrorContext,
1619 suggestions: &[ActionableSuggestion],
1620 ) -> Result<String> {
1621 let template = self.format_templates.get("default").unwrap();
1622
1623 let mut formatted = format!("ERROR: {error}\n\n");
1624
1625 formatted.push_str("CONTEXT:\n");
1627 formatted.push_str(&format!(
1628 " Pipeline: {}\n",
1629 context.pipeline_context.pipeline_name
1630 ));
1631 formatted.push_str(&format!(
1632 " Step: {}\n",
1633 context.pipeline_context.current_step
1634 ));
1635 formatted.push_str(&format!(
1636 " Data Shape: {:?}\n",
1637 context.data_context.input_shape
1638 ));
1639 formatted.push('\n');
1640
1641 if !suggestions.is_empty() {
1643 formatted.push_str("SUGGESTIONS:\n");
1644 for (i, suggestion) in suggestions.iter().enumerate() {
1645 formatted.push_str(&format!(
1646 "{}. {} (confidence: {:.1}%)\n",
1647 i + 1,
1648 suggestion.title,
1649 suggestion.confidence * 100.0
1650 ));
1651 formatted.push_str(&format!(" {}\n", suggestion.description));
1652 }
1653 }
1654
1655 Ok(formatted)
1656 }
1657}
1658
1659impl Default for EnhancedErrorContext {
1662 fn default() -> Self {
1663 Self {
1664 timestamp: SystemTime::now(),
1665 pipeline_context: PipelineContext::default(),
1666 data_context: DataContext::default(),
1667 environment_context: EnvironmentContext::default(),
1668 performance_context: PerformanceContext::default(),
1669 configuration_context: ConfigurationContext::default(),
1670 call_stack: Vec::new(),
1671 related_issues: Vec::new(),
1672 }
1673 }
1674}
1675
1676impl Default for PipelineContext {
1677 fn default() -> Self {
1678 Self {
1679 pipeline_name: "unknown".to_string(),
1680 current_step: "unknown".to_string(),
1681 step_index: 0,
1682 total_steps: 0,
1683 completed_steps: Vec::new(),
1684 pipeline_config: HashMap::new(),
1685 }
1686 }
1687}
1688
1689impl Default for DataContext {
1690 fn default() -> Self {
1691 Self {
1692 input_shape: Vec::new(),
1693 input_dtype: "unknown".to_string(),
1694 expected_shape: None,
1695 expected_dtype: None,
1696 data_statistics: DataStatistics {
1697 n_samples: 0,
1698 n_features: 0,
1699 means: Vec::new(),
1700 stds: Vec::new(),
1701 mins: Vec::new(),
1702 maxs: Vec::new(),
1703 },
1704 missing_values: MissingValueInfo {
1705 total_missing: 0,
1706 missing_per_feature: Vec::new(),
1707 missing_patterns: Vec::new(),
1708 },
1709 quality_metrics: DataQualityMetrics {
1710 quality_score: 0.0,
1711 completeness: 0.0,
1712 consistency: 0.0,
1713 validity: 0.0,
1714 quality_issues: Vec::new(),
1715 },
1716 }
1717 }
1718}
1719
1720impl Default for EnvironmentContext {
1721 fn default() -> Self {
1722 Self {
1723 os_info: "unknown".to_string(),
1724 available_memory: 0,
1725 cpu_info: "unknown".to_string(),
1726 runtime_version: "unknown".to_string(),
1727 package_versions: HashMap::new(),
1728 environment_variables: HashMap::new(),
1729 }
1730 }
1731}
1732
1733impl Default for PerformanceContext {
1734 fn default() -> Self {
1735 Self {
1736 memory_usage: 0,
1737 cpu_utilization: 0.0,
1738 execution_time: Duration::from_secs(0),
1739 bottlenecks: Vec::new(),
1740 }
1741 }
1742}
1743
1744impl Default for ErrorClassification {
1745 fn default() -> Self {
1746 Self {
1747 category: ErrorCategory::Unknown,
1748 severity: SeverityLevel::Medium,
1749 frequency: ErrorFrequency::Occasional,
1750 resolution_difficulty: DifficultyLevel::Medium,
1751 }
1752 }
1753}
1754
1755impl Default for ErrorTemplate {
1756 fn default() -> Self {
1757 Self {
1758 template_id: "default".to_string(),
1759 message_template: "{error}\n\nContext: {context}\n\nSuggestions: {suggestions}"
1760 .to_string(),
1761 context_sections: Vec::new(),
1762 suggestion_format: SuggestionFormat {
1763 max_suggestions: 5,
1764 include_code_examples: true,
1765 include_confidence_scores: true,
1766 include_time_estimates: false,
1767 },
1768 output_format: OutputFormat::PlainText,
1769 }
1770 }
1771}
1772
1773#[derive(Debug)]
1776struct EnvironmentContextProvider;
1777
1778impl EnvironmentContextProvider {
1779 fn new() -> Self {
1780 Self
1781 }
1782}
1783
1784impl ContextProvider for EnvironmentContextProvider {
1785 fn collect_context(&self, _error: &SklearsComposeError) -> Result<HashMap<String, String>> {
1786 let mut context = HashMap::new();
1787 context.insert("os".to_string(), std::env::consts::OS.to_string());
1788 context.insert("arch".to_string(), std::env::consts::ARCH.to_string());
1789 Ok(context)
1790 }
1791
1792 fn context_type(&self) -> ContextType {
1793 ContextType::Environment
1794 }
1795}
1796
1797#[derive(Debug)]
1798struct PerformanceContextProvider;
1799
1800impl PerformanceContextProvider {
1801 fn new() -> Self {
1802 Self
1803 }
1804}
1805
1806impl ContextProvider for PerformanceContextProvider {
1807 fn collect_context(&self, _error: &SklearsComposeError) -> Result<HashMap<String, String>> {
1808 let mut context = HashMap::new();
1809 context.insert("memory_usage".to_string(), "unknown".to_string());
1811 Ok(context)
1812 }
1813
1814 fn context_type(&self) -> ContextType {
1815 ContextType::Performance
1816 }
1817}
1818
1819#[derive(Debug)]
1822struct DataErrorSuggestionGenerator;
1823
1824impl DataErrorSuggestionGenerator {
1825 fn new() -> Self {
1826 Self
1827 }
1828}
1829
1830impl SuggestionGenerator for DataErrorSuggestionGenerator {
1831 fn generate_suggestions(
1832 &self,
1833 error: &SklearsComposeError,
1834 context: &EnhancedErrorContext,
1835 ) -> Result<Vec<ActionableSuggestion>> {
1836 let mut suggestions = Vec::new();
1837
1838 let error_str = error.to_string().to_lowercase();
1839
1840 if error_str.contains("shape") {
1841 suggestions.push(ActionableSuggestion {
1842 suggestion_id: "fix_data_shape".to_string(),
1843 title: "Fix Data Shape Mismatch".to_string(),
1844 description: "The input data shape doesn't match the expected shape. Check your data preprocessing steps.".to_string(),
1845 implementation_steps: vec![
1846 ImplementationStep {
1848 step_number: 1,
1849 description: "Check the shape of your input data".to_string(),
1850 code_snippet: Some("println!(\"Data shape: {:?}\", data.shape());".to_string()),
1851 command: None,
1852 expected_outcome: "Display the actual data shape".to_string(),
1853 validation_check: None,
1854 }
1855 ],
1856 code_examples: vec![
1857 CodeExample {
1859 language: "rust".to_string(),
1860 code: "let data = data.into_shape((n_samples, n_features))?;".to_string(),
1861 description: "Reshape data to correct dimensions".to_string(),
1862 file_path: None,
1863 }
1864 ],
1865 confidence: 0.9,
1866 estimated_time: <Duration as DurationExt>::from_mins(5),
1867 success_probability: 0.85,
1868 expertise_level: ExpertiseLevel::Beginner,
1869 prerequisites: Vec::new(),
1870 validation_method: Some("Check that data.shape() matches expected shape".to_string()),
1871 follow_up_suggestions: Vec::new(),
1872 });
1873 }
1874
1875 Ok(suggestions)
1876 }
1877
1878 fn error_types(&self) -> Vec<String> {
1879 vec!["DataError".to_string()]
1880 }
1881}
1882
1883#[derive(Debug)]
1884struct ConfigurationErrorSuggestionGenerator;
1885
1886impl ConfigurationErrorSuggestionGenerator {
1887 fn new() -> Self {
1888 Self
1889 }
1890}
1891
1892impl SuggestionGenerator for ConfigurationErrorSuggestionGenerator {
1893 fn generate_suggestions(
1894 &self,
1895 error: &SklearsComposeError,
1896 _context: &EnhancedErrorContext,
1897 ) -> Result<Vec<ActionableSuggestion>> {
1898 let mut suggestions = Vec::new();
1899
1900 suggestions.push(ActionableSuggestion {
1901 suggestion_id: "check_configuration".to_string(),
1902 title: "Review Configuration Settings".to_string(),
1903 description: "Check your configuration parameters for correct values and types."
1904 .to_string(),
1905 implementation_steps: vec![ImplementationStep {
1906 step_number: 1,
1907 description: "Review configuration documentation".to_string(),
1908 code_snippet: None,
1909 command: None,
1910 expected_outcome: "Understanding of correct configuration format".to_string(),
1911 validation_check: None,
1912 }],
1913 code_examples: Vec::new(),
1914 confidence: 0.7,
1915 estimated_time: <Duration as DurationExt>::from_mins(10),
1916 success_probability: 0.8,
1917 expertise_level: ExpertiseLevel::Intermediate,
1918 prerequisites: Vec::new(),
1919 validation_method: None,
1920 follow_up_suggestions: Vec::new(),
1921 });
1922
1923 Ok(suggestions)
1924 }
1925
1926 fn error_types(&self) -> Vec<String> {
1927 vec!["ConfigurationError".to_string()]
1928 }
1929}
1930
1931trait DurationExt {
1933 fn from_mins(minutes: u64) -> Duration;
1934}
1935
1936impl DurationExt for Duration {
1937 fn from_mins(minutes: u64) -> Duration {
1938 Duration::from_secs(minutes * 60)
1939 }
1940}
1941
1942#[allow(non_snake_case)]
1943#[cfg(test)]
1944mod tests {
1945 use super::*;
1946
1947 #[test]
1948 fn test_error_enhancer_creation() {
1949 let enhancer = ErrorMessageEnhancer::new();
1950 assert!(enhancer.config.enable_pattern_analysis);
1951 }
1952
1953 #[test]
1954 fn test_error_enhancement() {
1955 let enhancer = ErrorMessageEnhancer::new();
1956 let error = SklearsComposeError::InvalidConfiguration("Test error".to_string());
1957
1958 let result = enhancer.enhance_error(&error);
1959 assert!(result.is_ok());
1960
1961 let enhanced = result.unwrap();
1962 assert!(!enhanced.enhanced_message.is_empty());
1963 assert!(!enhanced.original_error.is_empty());
1964 }
1965
1966 #[test]
1967 fn test_context_collection() {
1968 let config = ContextCollectionConfig {
1969 enable_detailed_context: true,
1970 max_context_size: 1000,
1971 context_timeout: Duration::from_secs(1),
1972 };
1973
1974 let mut collector = ErrorContextCollector::new(config);
1975 let error = SklearsComposeError::InvalidConfiguration("Test".to_string());
1976
1977 let result = collector.collect_context(&error);
1978 assert!(result.is_ok());
1979 }
1980
1981 #[test]
1982 fn test_suggestion_generation() {
1983 let config = SuggestionEngineConfig {
1984 max_suggestions: 3,
1985 confidence_threshold: 0.5,
1986 enable_machine_learning: false,
1987 };
1988
1989 let engine = SuggestionEngine::new(config);
1990 let error = SklearsComposeError::InvalidData {
1991 reason: "shape mismatch".to_string(),
1992 };
1993 let context = EnhancedErrorContext::default();
1994
1995 let result = engine.generate_suggestions(&error, &context);
1996 assert!(result.is_ok());
1997 }
1998
1999 #[test]
2000 fn test_error_classification() {
2001 let config = PatternAnalysisConfig {
2002 max_patterns: 100,
2003 pattern_similarity_threshold: 0.8,
2004 learning_rate: 0.01,
2005 };
2006
2007 let mut analyzer = ErrorPatternAnalyzer::new(config);
2008 let error = SklearsComposeError::InvalidData {
2009 reason: "test".to_string(),
2010 };
2011 let context = EnhancedErrorContext::default();
2012
2013 let result = analyzer.analyze_error(&error, &context);
2014 assert!(result.is_ok());
2015
2016 let classification = result.unwrap();
2017 assert!(matches!(classification.category, ErrorCategory::DataError));
2018 }
2019
2020 #[test]
2021 fn test_recovery_strategies() {
2022 let config = RecoveryConfig {
2023 enable_auto_recovery: true,
2024 max_recovery_attempts: 3,
2025 recovery_timeout: Duration::from_secs(10),
2026 };
2027
2028 let advisor = RecoveryAdvisor::new(config);
2029 let error = SklearsComposeError::InvalidConfiguration("test".to_string());
2030 let context = EnhancedErrorContext::default();
2031
2032 let result = advisor.generate_recovery_strategies(&error, &context);
2033 assert!(result.is_ok());
2034
2035 let strategies = result.unwrap();
2036 assert!(!strategies.is_empty());
2037 }
2038
2039 #[test]
2040 fn test_error_formatting() {
2041 let config = FormatterConfig {
2042 default_language: "en".to_string(),
2043 include_technical_details: true,
2044 max_message_length: 1000,
2045 };
2046
2047 let formatter = ErrorFormatter::new(config);
2048 let error = SklearsComposeError::InvalidConfiguration("test error".to_string());
2049 let context = EnhancedErrorContext::default();
2050 let suggestions = Vec::new();
2051
2052 let result = formatter.format_error(&error, &context, &suggestions);
2053 assert!(result.is_ok());
2054
2055 let formatted = result.unwrap();
2056 assert!(formatted.contains("ERROR:"));
2057 assert!(formatted.contains("test error"));
2058 }
2059
2060 #[test]
2061 fn test_learning_from_resolution() {
2062 let enhancer = ErrorMessageEnhancer::new();
2063 let error = SklearsComposeError::InvalidConfiguration("test".to_string());
2064
2065 let result = enhancer.learn_from_resolution(&error, "test_suggestion", true);
2066 assert!(result.is_ok());
2067 }
2068
2069 #[test]
2070 fn test_statistics_export() {
2071 let enhancer = ErrorMessageEnhancer::new();
2072 let result = enhancer.export_statistics();
2073 assert!(result.is_ok());
2074
2075 let stats = result.unwrap();
2076 assert!(stats.success_rate >= 0.0 && stats.success_rate <= 1.0);
2077 }
2078
2079 #[test]
2080 fn test_context_providers() {
2081 let provider = EnvironmentContextProvider::new();
2082 let error = SklearsComposeError::InvalidConfiguration("test".to_string());
2083
2084 let result = provider.collect_context(&error);
2085 assert!(result.is_ok());
2086
2087 let context = result.unwrap();
2088 assert!(context.contains_key("os"));
2089 assert_eq!(provider.context_type(), ContextType::Environment);
2090 }
2091
2092 #[test]
2093 fn test_suggestion_generators() {
2094 let generator = DataErrorSuggestionGenerator::new();
2095 let error = SklearsComposeError::InvalidData {
2096 reason: "shape mismatch".to_string(),
2097 };
2098 let context = EnhancedErrorContext::default();
2099
2100 let result = generator.generate_suggestions(&error, &context);
2101 assert!(result.is_ok());
2102
2103 let suggestions = result.unwrap();
2104 assert!(!suggestions.is_empty());
2105 assert_eq!(generator.error_types(), vec!["DataError".to_string()]);
2106 }
2107}