sklears_compose/
enhanced_errors.rs

1//! Enhanced Error Handling with Actionable Suggestions
2//!
3//! This module provides comprehensive error handling for pipeline composition
4//! with detailed error messages, actionable suggestions, and debugging context.
5
6use sklears_core::prelude::SklearsError;
7use std::collections::HashMap;
8use std::fmt;
9
10/// Enhanced error types specific to pipeline composition
11#[derive(Debug, Clone)]
12pub enum PipelineError {
13    /// Configuration errors with suggestions
14    ConfigurationError {
15        message: String,
16        suggestions: Vec<String>,
17        context: ErrorContext,
18    },
19    /// Data compatibility issues
20    DataCompatibilityError {
21        expected: DataShape,
22        actual: DataShape,
23        stage: String,
24        suggestions: Vec<String>,
25    },
26    /// Pipeline structure errors
27    StructureError {
28        error_type: StructureErrorType,
29        affected_components: Vec<String>,
30        suggestions: Vec<String>,
31    },
32    /// Performance warnings that may impact execution
33    PerformanceWarning {
34        warning_type: PerformanceWarningType,
35        impact_level: ImpactLevel,
36        suggestions: Vec<String>,
37        metrics: Option<PerformanceMetrics>,
38    },
39    /// Resource constraint violations
40    ResourceError {
41        resource_type: ResourceType,
42        limit: f64,
43        current: f64,
44        component: String,
45        suggestions: Vec<String>,
46    },
47    /// Type safety violations in pipeline composition
48    TypeSafetyError {
49        violation_type: TypeViolationType,
50        expected_type: String,
51        actual_type: String,
52        stage: String,
53        suggestions: Vec<String>,
54    },
55}
56
57/// Error context information for debugging
58#[derive(Debug, Clone)]
59pub struct ErrorContext {
60    pub pipeline_stage: String,
61    pub component_name: String,
62    pub input_shape: Option<(usize, usize)>,
63    pub parameters: HashMap<String, String>,
64    pub stack_trace: Vec<String>,
65}
66
67/// Data shape information for compatibility checking
68#[derive(Debug, Clone, PartialEq)]
69pub struct DataShape {
70    pub samples: usize,
71    pub features: usize,
72    pub data_type: String,
73    pub missing_values: bool,
74}
75
76/// Types of pipeline structure errors
77#[derive(Debug, Clone)]
78pub enum StructureErrorType {
79    /// CyclicDependency
80    CyclicDependency,
81    /// MissingComponent
82    MissingComponent,
83    /// InvalidConnection
84    InvalidConnection,
85    /// DanglingNode
86    DanglingNode,
87    /// InconsistentFlow
88    InconsistentFlow,
89}
90
91/// Performance warning categories
92#[derive(Debug, Clone)]
93pub enum PerformanceWarningType {
94    /// MemoryUsage
95    MemoryUsage,
96    /// ComputationalComplexity
97    ComputationalComplexity,
98    /// NetworkBottleneck
99    NetworkBottleneck,
100    /// CacheInefficiency
101    CacheInefficiency,
102    /// SuboptimalConfiguration
103    SuboptimalConfiguration,
104}
105
106/// Impact levels for warnings and errors
107#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
108pub enum ImpactLevel {
109    /// Low
110    Low,
111    /// Medium
112    Medium,
113    /// High
114    High,
115    /// Critical
116    Critical,
117}
118
119/// Resource types for constraint checking
120#[derive(Debug, Clone)]
121pub enum ResourceType {
122    /// Memory
123    Memory,
124    /// CPU
125    CPU,
126    /// GPU
127    GPU,
128    /// Disk
129    Disk,
130    /// NetworkBandwidth
131    NetworkBandwidth,
132}
133
134/// Performance metrics for context
135#[derive(Debug, Clone)]
136pub struct PerformanceMetrics {
137    pub execution_time_ms: f64,
138    pub memory_usage_mb: f64,
139    pub cpu_utilization: f64,
140    pub cache_hit_ratio: f64,
141}
142
143/// Type safety violation categories
144#[derive(Debug, Clone)]
145pub enum TypeViolationType {
146    /// IncompatibleInputType
147    IncompatibleInputType,
148    /// MismatchedOutputType
149    MismatchedOutputType,
150    /// UnsupportedTransformation
151    UnsupportedTransformation,
152    /// InvalidParameterType
153    InvalidParameterType,
154}
155
156/// Enhanced error builder for creating detailed error messages
157pub struct EnhancedErrorBuilder {
158    error_type: Option<PipelineError>,
159    suggestions: Vec<String>,
160    context: Option<ErrorContext>,
161}
162
163impl Default for EnhancedErrorBuilder {
164    fn default() -> Self {
165        Self::new()
166    }
167}
168
169impl EnhancedErrorBuilder {
170    #[must_use]
171    pub fn new() -> Self {
172        Self {
173            error_type: None,
174            suggestions: Vec::new(),
175            context: None,
176        }
177    }
178
179    /// Create configuration error with detailed suggestions
180    #[must_use]
181    pub fn configuration_error(mut self, message: &str) -> Self {
182        let suggestions = self.generate_configuration_suggestions(message);
183        self.error_type = Some(PipelineError::ConfigurationError {
184            message: message.to_string(),
185            suggestions,
186            context: self.context.clone().unwrap_or_default(),
187        });
188        self
189    }
190
191    /// Create data compatibility error with shape analysis
192    #[must_use]
193    pub fn data_compatibility_error(
194        mut self,
195        expected: DataShape,
196        actual: DataShape,
197        stage: &str,
198    ) -> Self {
199        let suggestions = self.generate_compatibility_suggestions(&expected, &actual, stage);
200        self.error_type = Some(PipelineError::DataCompatibilityError {
201            expected,
202            actual,
203            stage: stage.to_string(),
204            suggestions,
205        });
206        self
207    }
208
209    /// Create structure error with component analysis
210    #[must_use]
211    pub fn structure_error(
212        mut self,
213        error_type: StructureErrorType,
214        affected_components: Vec<String>,
215    ) -> Self {
216        let suggestions = self.generate_structure_suggestions(&error_type, &affected_components);
217        self.error_type = Some(PipelineError::StructureError {
218            error_type,
219            affected_components,
220            suggestions,
221        });
222        self
223    }
224
225    /// Create performance warning with optimization suggestions
226    #[must_use]
227    pub fn performance_warning(
228        mut self,
229        warning_type: PerformanceWarningType,
230        impact_level: ImpactLevel,
231        metrics: Option<PerformanceMetrics>,
232    ) -> Self {
233        let suggestions =
234            self.generate_performance_suggestions(&warning_type, &impact_level, &metrics);
235        self.error_type = Some(PipelineError::PerformanceWarning {
236            warning_type,
237            impact_level,
238            suggestions,
239            metrics,
240        });
241        self
242    }
243
244    /// Create resource error with scaling suggestions
245    #[must_use]
246    pub fn resource_error(
247        mut self,
248        resource_type: ResourceType,
249        limit: f64,
250        current: f64,
251        component: &str,
252    ) -> Self {
253        let suggestions =
254            self.generate_resource_suggestions(&resource_type, limit, current, component);
255        self.error_type = Some(PipelineError::ResourceError {
256            resource_type,
257            limit,
258            current,
259            component: component.to_string(),
260            suggestions,
261        });
262        self
263    }
264
265    /// Create type safety error with type conversion suggestions
266    #[must_use]
267    pub fn type_safety_error(
268        mut self,
269        violation_type: TypeViolationType,
270        expected_type: &str,
271        actual_type: &str,
272        stage: &str,
273    ) -> Self {
274        let suggestions = self.generate_type_safety_suggestions(
275            &violation_type,
276            expected_type,
277            actual_type,
278            stage,
279        );
280        self.error_type = Some(PipelineError::TypeSafetyError {
281            violation_type,
282            expected_type: expected_type.to_string(),
283            actual_type: actual_type.to_string(),
284            stage: stage.to_string(),
285            suggestions,
286        });
287        self
288    }
289
290    /// Add custom suggestion
291    #[must_use]
292    pub fn suggestion(mut self, suggestion: &str) -> Self {
293        self.suggestions.push(suggestion.to_string());
294        self
295    }
296
297    /// Add error context
298    #[must_use]
299    pub fn context(mut self, context: ErrorContext) -> Self {
300        self.context = Some(context);
301        self
302    }
303
304    /// Build the enhanced error
305    #[must_use]
306    pub fn build(self) -> PipelineError {
307        // Add any custom suggestions to the error and update context
308        if let Some(mut error) = self.error_type {
309            // Add suggestions to all error types
310            match &mut error {
311                PipelineError::ConfigurationError { suggestions, .. } => {
312                    suggestions.extend(self.suggestions);
313                }
314                PipelineError::DataCompatibilityError { suggestions, .. } => {
315                    suggestions.extend(self.suggestions);
316                }
317                PipelineError::StructureError { suggestions, .. } => {
318                    suggestions.extend(self.suggestions);
319                }
320                PipelineError::PerformanceWarning { suggestions, .. } => {
321                    suggestions.extend(self.suggestions);
322                }
323                PipelineError::ResourceError { suggestions, .. } => {
324                    suggestions.extend(self.suggestions);
325                }
326                PipelineError::TypeSafetyError { suggestions, .. } => {
327                    suggestions.extend(self.suggestions);
328                }
329            }
330            // Update context for ConfigurationError specifically
331            if let PipelineError::ConfigurationError { context, .. } = &mut error {
332                if let Some(new_context) = self.context {
333                    *context = new_context;
334                }
335            }
336            error
337        } else {
338            // Default error if none specified
339            PipelineError::ConfigurationError {
340                message: "Unknown pipeline error".to_string(),
341                suggestions: self.suggestions,
342                context: self.context.unwrap_or_default(),
343            }
344        }
345    }
346
347    /// Generate configuration-specific suggestions
348    fn generate_configuration_suggestions(&self, message: &str) -> Vec<String> {
349        let mut suggestions = Vec::new();
350
351        if message.contains("parameter") {
352            suggestions.push("Check parameter names and types in the documentation".to_string());
353            suggestions.push("Use the builder pattern to set parameters safely".to_string());
354            suggestions.push("Validate parameter ranges before setting".to_string());
355        }
356
357        if message.contains("missing") {
358            suggestions
359                .push("Ensure all required components are added to the pipeline".to_string());
360            suggestions.push("Check the pipeline construction order".to_string());
361        }
362
363        if message.contains("incompatible") {
364            suggestions
365                .push("Verify component compatibility using the validation tools".to_string());
366            suggestions
367                .push("Consider adding adapter components between incompatible stages".to_string());
368        }
369
370        suggestions
371    }
372
373    /// Generate data compatibility suggestions
374    fn generate_compatibility_suggestions(
375        &self,
376        expected: &DataShape,
377        actual: &DataShape,
378        stage: &str,
379    ) -> Vec<String> {
380        let mut suggestions = Vec::new();
381
382        if expected.features != actual.features {
383            suggestions.push(format!(
384                "Feature count mismatch in '{}': expected {}, got {}",
385                stage, expected.features, actual.features
386            ));
387            suggestions.push(
388                "Consider adding feature selection or expansion before this stage".to_string(),
389            );
390            suggestions.push(
391                "Check if previous pipeline stages modified the feature count unexpectedly"
392                    .to_string(),
393            );
394        }
395
396        if expected.samples != actual.samples {
397            suggestions.push(format!(
398                "Sample count mismatch in '{}': expected {}, got {}",
399                stage, expected.samples, actual.samples
400            ));
401            suggestions.push("Verify data splitting and sampling operations".to_string());
402        }
403
404        if expected.data_type != actual.data_type {
405            suggestions.push(format!(
406                "Data type mismatch in '{}': expected {}, got {}",
407                stage, expected.data_type, actual.data_type
408            ));
409            suggestions.push("Add type conversion transformers before this stage".to_string());
410        }
411
412        if actual.missing_values && !expected.missing_values {
413            suggestions.push(
414                "Handle missing values using imputation or removal before this stage".to_string(),
415            );
416            suggestions
417                .push("Consider using robust algorithms that handle missing data".to_string());
418        }
419
420        suggestions
421    }
422
423    /// Generate structure-specific suggestions
424    fn generate_structure_suggestions(
425        &self,
426        error_type: &StructureErrorType,
427        affected_components: &[String],
428    ) -> Vec<String> {
429        let mut suggestions = Vec::new();
430
431        match error_type {
432            StructureErrorType::CyclicDependency => {
433                suggestions.push("Remove circular dependencies between components".to_string());
434                suggestions
435                    .push("Use topological sorting to validate pipeline structure".to_string());
436                suggestions.push(format!(
437                    "Affected components: {}",
438                    affected_components.join(", ")
439                ));
440            }
441            StructureErrorType::MissingComponent => {
442                suggestions.push("Add the missing component to the pipeline".to_string());
443                suggestions.push("Check component names for typos".to_string());
444                suggestions
445                    .push("Verify component registration in the pipeline builder".to_string());
446            }
447            StructureErrorType::InvalidConnection => {
448                suggestions.push("Check connection compatibility between components".to_string());
449                suggestions.push("Ensure output types match input requirements".to_string());
450                suggestions.push("Consider adding adapter components".to_string());
451            }
452            StructureErrorType::DanglingNode => {
453                suggestions.push("Connect all nodes to the main pipeline flow".to_string());
454                suggestions.push("Remove unused components or connect them properly".to_string());
455            }
456            StructureErrorType::InconsistentFlow => {
457                suggestions.push("Review the entire pipeline flow for consistency".to_string());
458                suggestions.push("Use pipeline validation tools before execution".to_string());
459            }
460        }
461
462        suggestions
463    }
464
465    /// Generate performance optimization suggestions
466    fn generate_performance_suggestions(
467        &self,
468        warning_type: &PerformanceWarningType,
469        impact_level: &ImpactLevel,
470        metrics: &Option<PerformanceMetrics>,
471    ) -> Vec<String> {
472        let mut suggestions = Vec::new();
473
474        match warning_type {
475            PerformanceWarningType::MemoryUsage => {
476                suggestions
477                    .push("Consider using streaming processing for large datasets".to_string());
478                suggestions.push("Enable memory-efficient pipeline execution".to_string());
479                suggestions.push("Use data chunking to reduce memory footprint".to_string());
480
481                if let Some(metrics) = metrics {
482                    if metrics.memory_usage_mb > 1000.0 {
483                        suggestions.push(
484                            "Memory usage exceeds 1GB - consider distributed processing"
485                                .to_string(),
486                        );
487                    }
488                }
489            }
490            PerformanceWarningType::ComputationalComplexity => {
491                suggestions.push("Use parallel processing where possible".to_string());
492                suggestions.push("Consider simpler algorithms for large datasets".to_string());
493                suggestions.push("Enable SIMD optimizations if available".to_string());
494            }
495            PerformanceWarningType::NetworkBottleneck => {
496                suggestions.push("Implement result caching to reduce network calls".to_string());
497                suggestions.push("Use connection pooling for external services".to_string());
498                suggestions.push("Consider local processing alternatives".to_string());
499            }
500            PerformanceWarningType::CacheInefficiency => {
501                suggestions.push("Optimize cache configuration and size".to_string());
502                suggestions.push("Review cache key generation strategy".to_string());
503                suggestions.push("Consider different cache eviction policies".to_string());
504
505                if let Some(metrics) = metrics {
506                    if metrics.cache_hit_ratio < 0.5 {
507                        suggestions.push(format!(
508                            "Low cache hit ratio ({:.1}%) - review caching strategy",
509                            metrics.cache_hit_ratio * 100.0
510                        ));
511                    }
512                }
513            }
514            PerformanceWarningType::SuboptimalConfiguration => {
515                suggestions
516                    .push("Run hyperparameter optimization to find better settings".to_string());
517                suggestions.push("Use AutoML tools for automatic configuration tuning".to_string());
518                suggestions.push("Profile different configuration options".to_string());
519            }
520        }
521
522        match impact_level {
523            ImpactLevel::Critical => {
524                suggestions.insert(
525                    0,
526                    "🚨 CRITICAL: This issue requires immediate attention".to_string(),
527                );
528            }
529            ImpactLevel::High => {
530                suggestions.insert(0, "⚠️ HIGH PRIORITY: Address this issue soon".to_string());
531            }
532            _ => {}
533        }
534
535        suggestions
536    }
537
538    /// Generate resource constraint suggestions
539    fn generate_resource_suggestions(
540        &self,
541        resource_type: &ResourceType,
542        limit: f64,
543        current: f64,
544        component: &str,
545    ) -> Vec<String> {
546        let mut suggestions = Vec::new();
547        let utilization = (current / limit) * 100.0;
548
549        suggestions.push(format!(
550            "Resource utilization: {utilization:.1}% ({current:.2}/{limit:.2}) in component '{component}'"
551        ));
552
553        match resource_type {
554            ResourceType::Memory => {
555                suggestions.push("Reduce batch size to lower memory usage".to_string());
556                suggestions.push("Enable streaming processing mode".to_string());
557                suggestions.push("Use memory-mapped files for large datasets".to_string());
558                if utilization > 90.0 {
559                    suggestions.push(
560                        "Consider upgrading system memory or using distributed processing"
561                            .to_string(),
562                    );
563                }
564            }
565            ResourceType::CPU => {
566                suggestions.push("Reduce computational complexity of algorithms".to_string());
567                suggestions.push("Enable parallel processing across multiple cores".to_string());
568                suggestions.push("Use approximate algorithms for faster computation".to_string());
569            }
570            ResourceType::GPU => {
571                suggestions.push("Optimize GPU memory allocation and transfers".to_string());
572                suggestions
573                    .push("Use mixed precision training to reduce GPU memory usage".to_string());
574                suggestions.push("Consider model parallelism for large models".to_string());
575            }
576            ResourceType::Disk => {
577                suggestions.push("Enable data compression to reduce disk usage".to_string());
578                suggestions.push("Use temporary file cleanup strategies".to_string());
579                suggestions.push("Consider cloud storage for large datasets".to_string());
580            }
581            ResourceType::NetworkBandwidth => {
582                suggestions.push("Implement data compression for network transfers".to_string());
583                suggestions.push("Use local caching to reduce network usage".to_string());
584                suggestions.push("Consider edge processing to minimize data movement".to_string());
585            }
586        }
587
588        suggestions
589    }
590
591    /// Generate type safety suggestions
592    fn generate_type_safety_suggestions(
593        &self,
594        violation_type: &TypeViolationType,
595        expected_type: &str,
596        actual_type: &str,
597        stage: &str,
598    ) -> Vec<String> {
599        let mut suggestions = Vec::new();
600
601        suggestions.push(format!(
602            "Type mismatch in '{stage}': expected '{expected_type}', got '{actual_type}'"
603        ));
604
605        match violation_type {
606            TypeViolationType::IncompatibleInputType => {
607                suggestions.push("Add type conversion transformer before this stage".to_string());
608                suggestions
609                    .push("Check the output type of the previous pipeline stage".to_string());
610                suggestions.push("Use type adapters for incompatible formats".to_string());
611            }
612            TypeViolationType::MismatchedOutputType => {
613                suggestions.push("Verify the component's output type specification".to_string());
614                suggestions.push("Use type casting or conversion as the final step".to_string());
615                suggestions.push("Check if the component configuration is correct".to_string());
616            }
617            TypeViolationType::UnsupportedTransformation => {
618                suggestions.push(
619                    "Use a different transformation that supports this data type".to_string(),
620                );
621                suggestions.push("Preprocess the data to a supported type".to_string());
622                suggestions.push("Consider using a custom transformer".to_string());
623            }
624            TypeViolationType::InvalidParameterType => {
625                suggestions.push("Check parameter type requirements in documentation".to_string());
626                suggestions.push("Use proper type conversion for parameter values".to_string());
627                suggestions
628                    .push("Validate parameter types before pipeline construction".to_string());
629            }
630        }
631
632        // Add common type conversion suggestions
633        if expected_type.contains("float") && actual_type.contains("int") {
634            suggestions
635                .push("Convert integer values to float using .mapv(|x| x as f64)".to_string());
636        } else if expected_type.contains("int") && actual_type.contains("float") {
637            suggestions
638                .push("Convert float values to integer (with rounding if needed)".to_string());
639        }
640
641        suggestions
642    }
643}
644
645impl Default for ErrorContext {
646    fn default() -> Self {
647        Self {
648            pipeline_stage: "unknown".to_string(),
649            component_name: "unknown".to_string(),
650            input_shape: None,
651            parameters: HashMap::new(),
652            stack_trace: Vec::new(),
653        }
654    }
655}
656
657impl fmt::Display for PipelineError {
658    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659        match self {
660            PipelineError::ConfigurationError {
661                message,
662                suggestions,
663                context,
664            } => {
665                writeln!(f, "🔧 Configuration Error: {message}")?;
666                writeln!(
667                    f,
668                    "   Component: {} (Stage: {})",
669                    context.component_name, context.pipeline_stage
670                )?;
671                if !suggestions.is_empty() {
672                    writeln!(f, "💡 Suggestions:")?;
673                    for suggestion in suggestions {
674                        writeln!(f, "   • {suggestion}")?;
675                    }
676                }
677            }
678            PipelineError::DataCompatibilityError {
679                expected,
680                actual,
681                stage,
682                suggestions,
683            } => {
684                writeln!(f, "📊 Data Compatibility Error in stage '{stage}':")?;
685                writeln!(
686                    f,
687                    "   Expected: {} samples × {} features ({})",
688                    expected.samples, expected.features, expected.data_type
689                )?;
690                writeln!(
691                    f,
692                    "   Actual:   {} samples × {} features ({})",
693                    actual.samples, actual.features, actual.data_type
694                )?;
695                if !suggestions.is_empty() {
696                    writeln!(f, "💡 Suggestions:")?;
697                    for suggestion in suggestions {
698                        writeln!(f, "   • {suggestion}")?;
699                    }
700                }
701            }
702            PipelineError::StructureError {
703                error_type,
704                affected_components,
705                suggestions,
706            } => {
707                writeln!(f, "🏗️ Pipeline Structure Error: {error_type:?}")?;
708                writeln!(
709                    f,
710                    "   Affected components: {}",
711                    affected_components.join(", ")
712                )?;
713                if !suggestions.is_empty() {
714                    writeln!(f, "💡 Suggestions:")?;
715                    for suggestion in suggestions {
716                        writeln!(f, "   • {suggestion}")?;
717                    }
718                }
719            }
720            PipelineError::PerformanceWarning {
721                warning_type,
722                impact_level,
723                suggestions,
724                metrics,
725            } => {
726                writeln!(
727                    f,
728                    "⚡ Performance Warning: {warning_type:?} (Impact: {impact_level:?})"
729                )?;
730                if let Some(metrics) = metrics {
731                    writeln!(
732                        f,
733                        "   Execution time: {:.2}ms, Memory: {:.1}MB, CPU: {:.1}%",
734                        metrics.execution_time_ms, metrics.memory_usage_mb, metrics.cpu_utilization
735                    )?;
736                }
737                if !suggestions.is_empty() {
738                    writeln!(f, "💡 Suggestions:")?;
739                    for suggestion in suggestions {
740                        writeln!(f, "   • {suggestion}")?;
741                    }
742                }
743            }
744            PipelineError::ResourceError {
745                resource_type,
746                limit,
747                current,
748                component,
749                suggestions,
750            } => {
751                let utilization = (current / limit) * 100.0;
752                writeln!(
753                    f,
754                    "🔋 Resource Error: {resource_type:?} constraint violated in '{component}'"
755                )?;
756                writeln!(f, "   Usage: {current:.2}/{limit:.2} ({utilization:.1}%)")?;
757                if !suggestions.is_empty() {
758                    writeln!(f, "💡 Suggestions:")?;
759                    for suggestion in suggestions {
760                        writeln!(f, "   • {suggestion}")?;
761                    }
762                }
763            }
764            PipelineError::TypeSafetyError {
765                violation_type,
766                expected_type,
767                actual_type,
768                stage,
769                suggestions,
770            } => {
771                writeln!(
772                    f,
773                    "🛡️ Type Safety Error: {violation_type:?} in stage '{stage}'"
774                )?;
775                writeln!(f, "   Expected: {expected_type}, Got: {actual_type}")?;
776                if !suggestions.is_empty() {
777                    writeln!(f, "💡 Suggestions:")?;
778                    for suggestion in suggestions {
779                        writeln!(f, "   • {suggestion}")?;
780                    }
781                }
782            }
783        }
784        Ok(())
785    }
786}
787
788impl From<PipelineError> for SklearsError {
789    fn from(error: PipelineError) -> Self {
790        SklearsError::InvalidInput(error.to_string())
791    }
792}
793
794/// Convenience functions for creating enhanced errors
795impl PipelineError {
796    /// Create a configuration error with suggestions
797    #[must_use]
798    pub fn configuration(message: &str) -> Self {
799        EnhancedErrorBuilder::new()
800            .configuration_error(message)
801            .build()
802    }
803
804    /// Create a data compatibility error
805    #[must_use]
806    pub fn data_compatibility(expected: DataShape, actual: DataShape, stage: &str) -> Self {
807        EnhancedErrorBuilder::new()
808            .data_compatibility_error(expected, actual, stage)
809            .build()
810    }
811
812    /// Create a performance warning
813    #[must_use]
814    pub fn performance_warning(
815        warning_type: PerformanceWarningType,
816        impact_level: ImpactLevel,
817        metrics: Option<PerformanceMetrics>,
818    ) -> Self {
819        EnhancedErrorBuilder::new()
820            .performance_warning(warning_type, impact_level, metrics)
821            .build()
822    }
823}
824
825#[allow(non_snake_case)]
826#[cfg(test)]
827mod tests {
828    use super::*;
829
830    #[test]
831    fn test_configuration_error_creation() {
832        let error = PipelineError::configuration("Invalid parameter 'learning_rate'");
833
834        let error_string = error.to_string();
835        assert!(error_string.contains("Configuration Error"));
836        assert!(error_string.contains("Invalid parameter"));
837        assert!(error_string.contains("💡 Suggestions:"));
838    }
839
840    #[test]
841    fn test_data_compatibility_error() {
842        let expected = DataShape {
843            samples: 100,
844            features: 10,
845            data_type: "float64".to_string(),
846            missing_values: false,
847        };
848        let actual = DataShape {
849            samples: 100,
850            features: 8,
851            data_type: "float64".to_string(),
852            missing_values: true,
853        };
854
855        let error = PipelineError::data_compatibility(expected, actual, "feature_selection");
856        let error_string = error.to_string();
857
858        assert!(error_string.contains("Data Compatibility Error"));
859        assert!(error_string.contains("feature_selection"));
860        assert!(error_string.contains("10 features"));
861        assert!(error_string.contains("8 features"));
862    }
863
864    #[test]
865    fn test_enhanced_error_builder() {
866        let context = ErrorContext {
867            pipeline_stage: "preprocessing".to_string(),
868            component_name: "scaler".to_string(),
869            input_shape: Some((100, 5)),
870            parameters: HashMap::new(),
871            stack_trace: Vec::new(),
872        };
873
874        let error = EnhancedErrorBuilder::new()
875            .configuration_error("Missing required parameter")
876            .suggestion("Check the documentation for required parameters")
877            .context(context)
878            .build();
879
880        let error_string = error.to_string();
881        println!("Error string: {}", error_string);
882        assert!(error_string.contains("Configuration Error"));
883        assert!(error_string.contains("Stage: preprocessing"));
884        assert!(error_string.contains("scaler"));
885    }
886}