sklears_compose/
configuration_validation.rs

1//! Enhanced Configuration Validation Framework
2//!
3//! This module provides comprehensive compile-time and runtime validation for pipeline
4//! configurations, parameter constraints, and component compatibility. It ensures that
5//! pipeline configurations are correct before execution and provides detailed diagnostics
6//! for configuration errors.
7
8use serde::{Deserialize, Serialize};
9use sklears_core::traits::Estimator;
10use std::collections::HashMap;
11use std::fmt;
12use std::marker::PhantomData;
13
14/// Validation severity levels
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
16pub enum ValidationSeverity {
17    /// Info
18    Info,
19    /// Warning
20    Warning,
21    /// Error
22    Error,
23    /// Critical
24    Critical,
25}
26
27/// Configuration validation result
28#[derive(Debug, Clone)]
29pub struct ValidationResult {
30    pub severity: ValidationSeverity,
31    pub component: String,
32    pub field: String,
33    pub message: String,
34    pub suggestions: Vec<String>,
35    pub error_code: String,
36}
37
38impl ValidationResult {
39    pub fn new(
40        severity: ValidationSeverity,
41        component: impl Into<String>,
42        field: impl Into<String>,
43        message: impl Into<String>,
44    ) -> Self {
45        Self {
46            severity,
47            component: component.into(),
48            field: field.into(),
49            message: message.into(),
50            suggestions: Vec::new(),
51            error_code: "VALIDATION_ERROR".to_string(),
52        }
53    }
54
55    #[must_use]
56    pub fn with_suggestions(mut self, suggestions: Vec<String>) -> Self {
57        self.suggestions = suggestions;
58        self
59    }
60
61    pub fn with_error_code(mut self, code: impl Into<String>) -> Self {
62        self.error_code = code.into();
63        self
64    }
65
66    #[must_use]
67    pub fn is_error(&self) -> bool {
68        matches!(
69            self.severity,
70            ValidationSeverity::Error | ValidationSeverity::Critical
71        )
72    }
73}
74
75/// Comprehensive validation report
76#[derive(Debug, Clone)]
77pub struct ValidationReport {
78    pub results: Vec<ValidationResult>,
79    pub summary: ValidationSummary,
80}
81
82#[derive(Debug, Clone)]
83pub struct ValidationSummary {
84    pub total_checks: usize,
85    pub passed: usize,
86    pub warnings: usize,
87    pub errors: usize,
88    pub critical: usize,
89    pub overall_status: ValidationStatus,
90}
91
92#[derive(Debug, Clone, PartialEq)]
93pub enum ValidationStatus {
94    /// Passed
95    Passed,
96    /// PassedWithWarnings
97    PassedWithWarnings,
98    /// Failed
99    Failed,
100    /// Critical
101    Critical,
102}
103
104impl Default for ValidationReport {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110impl ValidationReport {
111    #[must_use]
112    pub fn new() -> Self {
113        Self {
114            results: Vec::new(),
115            summary: ValidationSummary {
116                total_checks: 0,
117                passed: 0,
118                warnings: 0,
119                errors: 0,
120                critical: 0,
121                overall_status: ValidationStatus::Passed,
122            },
123        }
124    }
125
126    pub fn add_result(&mut self, result: ValidationResult) {
127        match result.severity {
128            ValidationSeverity::Info => {}
129            ValidationSeverity::Warning => self.summary.warnings += 1,
130            ValidationSeverity::Error => self.summary.errors += 1,
131            ValidationSeverity::Critical => self.summary.critical += 1,
132        }
133        self.results.push(result);
134        self.update_summary();
135    }
136
137    fn update_summary(&mut self) {
138        self.summary.total_checks = self.results.len();
139        self.summary.passed = self.summary.total_checks
140            - self.summary.warnings
141            - self.summary.errors
142            - self.summary.critical;
143
144        self.summary.overall_status = if self.summary.critical > 0 {
145            ValidationStatus::Critical
146        } else if self.summary.errors > 0 {
147            ValidationStatus::Failed
148        } else if self.summary.warnings > 0 {
149            ValidationStatus::PassedWithWarnings
150        } else {
151            ValidationStatus::Passed
152        };
153    }
154
155    #[must_use]
156    pub fn has_errors(&self) -> bool {
157        self.summary.errors > 0 || self.summary.critical > 0
158    }
159
160    #[must_use]
161    pub fn display_summary(&self) -> String {
162        format!(
163            "Validation Summary: {} checks, {} passed, {} warnings, {} errors, {} critical (Status: {:?})",
164            self.summary.total_checks,
165            self.summary.passed,
166            self.summary.warnings,
167            self.summary.errors,
168            self.summary.critical,
169            self.summary.overall_status
170        )
171    }
172}
173
174/// Configuration validator trait for type-safe validation
175pub trait ConfigurationValidator<T> {
176    fn validate(&self, config: &T) -> ValidationReport;
177    fn validate_field(&self, field_name: &str, value: &dyn std::fmt::Debug) -> ValidationReport;
178    fn get_validation_schema(&self) -> ValidationSchema;
179}
180
181/// Validation schema definition
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct ValidationSchema {
184    pub name: String,
185    pub version: String,
186    pub fields: HashMap<String, FieldConstraints>,
187    pub dependencies: Vec<DependencyConstraint>,
188    pub custom_rules: Vec<CustomValidationRule>,
189}
190
191/// Field-level validation constraints
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct FieldConstraints {
194    pub required: bool,
195    pub field_type: FieldType,
196    pub constraints: Vec<Constraint>,
197    pub description: String,
198    pub examples: Vec<String>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub enum FieldType {
203    /// Integer
204    Integer,
205    /// Float
206    Float,
207    /// String
208    String,
209    /// Boolean
210    Boolean,
211    /// Array
212    Array(Box<FieldType>),
213    /// Object
214    Object(String), // Schema name reference
215    /// Enum
216    Enum(Vec<String>),
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub enum Constraint {
221    /// Range
222    Range { min: f64, max: f64 },
223    /// Length
224    Length { min: usize, max: usize },
225    /// Pattern
226    Pattern(String), // Regex pattern
227    /// OneOf
228    OneOf(Vec<String>),
229    /// Custom
230    Custom(String), // Custom validation function name
231}
232
233/// Cross-field dependency constraints
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct DependencyConstraint {
236    pub name: String,
237    pub condition: String, // Expression that must be true
238    pub error_message: String,
239    pub severity: ValidationSeverity,
240}
241
242/// Custom validation rules
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct CustomValidationRule {
245    pub name: String,
246    pub description: String,
247    pub rule_type: RuleType,
248    pub parameters: HashMap<String, String>,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub enum RuleType {
253    /// ParameterCompatibility
254    ParameterCompatibility,
255    /// PerformanceWarning
256    PerformanceWarning,
257    /// SecurityCheck
258    SecurityCheck,
259    /// ResourceValidation
260    ResourceValidation,
261}
262
263/// Enhanced compile-time configuration validator with type-level constraints
264pub struct CompileTimeValidator<T> {
265    schema: ValidationSchema,
266    _phantom: PhantomData<T>,
267}
268
269impl<T> CompileTimeValidator<T> {
270    #[must_use]
271    pub fn new(schema: ValidationSchema) -> Self {
272        Self {
273            schema,
274            _phantom: PhantomData,
275        }
276    }
277
278    /// Validate configuration at compile time where possible
279    pub fn validate_static(&self, config: &T) -> ValidationReport
280    where
281        T: fmt::Debug,
282    {
283        // Add static validation logic here
284        // This would be expanded with actual compile-time checks
285
286        ValidationReport::new()
287    }
288}
289
290/// Type-level validation traits for compile-time guarantees
291pub trait ValidConfig {
292    type Error;
293    const IS_VALID: bool;
294
295    fn validate_config() -> Result<(), Self::Error>;
296}
297
298/// Phantom type for validated configurations
299pub struct ValidatedConfig<T> {
300    inner: T,
301}
302
303impl<T> ValidatedConfig<T>
304where
305    T: ValidConfig,
306{
307    /// Create a validated configuration
308    pub fn new(config: T) -> Result<Self, T::Error> {
309        if T::IS_VALID {
310            Ok(ValidatedConfig { inner: config })
311        } else {
312            Err(T::validate_config().unwrap_err())
313        }
314    }
315
316    /// Access the inner configuration
317    pub fn inner(&self) -> &T {
318        &self.inner
319    }
320
321    /// Consume and return the inner configuration
322    pub fn into_inner(self) -> T {
323        self.inner
324    }
325}
326
327/// Type-level parameter constraints using const generics
328pub trait ParameterConstraints<const MIN: i32, const MAX: i32> {
329    #[must_use]
330    fn validate_range(value: i32) -> bool {
331        value >= MIN && value <= MAX
332    }
333}
334
335/// Compile-time validated parameter with range constraints
336#[derive(Debug, Clone, Copy)]
337pub struct ValidatedParameter<const MIN: i32, const MAX: i32> {
338    value: i32,
339}
340
341impl<const MIN: i32, const MAX: i32> ValidatedParameter<MIN, MAX> {
342    /// Create a validated parameter at compile time if possible
343    #[must_use]
344    pub const fn new(value: i32) -> Option<Self> {
345        if value >= MIN && value <= MAX {
346            Some(ValidatedParameter { value })
347        } else {
348            None
349        }
350    }
351
352    /// Create a validated parameter with runtime check
353    pub fn new_runtime(value: i32) -> Result<Self, ParameterValidationError> {
354        if value >= MIN && value <= MAX {
355            Ok(ValidatedParameter { value })
356        } else {
357            Err(ParameterValidationError::OutOfRange {
358                value,
359                min: MIN,
360                max: MAX,
361            })
362        }
363    }
364
365    /// Get the parameter value
366    #[must_use]
367    pub const fn value(&self) -> i32 {
368        self.value
369    }
370}
371
372/// Enhanced parameter validation errors
373#[derive(Debug, Clone, PartialEq, Eq)]
374pub enum ParameterValidationError {
375    /// OutOfRange
376    OutOfRange { value: i32, min: i32, max: i32 },
377    /// InvalidType
378    InvalidType { expected: String, actual: String },
379    /// MissingRequired
380    MissingRequired { parameter: String },
381    /// DependencyViolation
382    DependencyViolation {
383        parameter: String,
384        dependency: String,
385    },
386}
387
388impl fmt::Display for ParameterValidationError {
389    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        match self {
391            ParameterValidationError::OutOfRange { value, min, max } => {
392                write!(f, "Parameter value {value} is out of range [{min}, {max}]")
393            }
394            ParameterValidationError::InvalidType { expected, actual } => {
395                write!(f, "Expected type '{expected}', found '{actual}'")
396            }
397            ParameterValidationError::MissingRequired { parameter } => {
398                write!(f, "Required parameter '{parameter}' is missing")
399            }
400            ParameterValidationError::DependencyViolation {
401                parameter,
402                dependency,
403            } => {
404                write!(
405                    f,
406                    "Parameter '{parameter}' violates dependency constraint '{dependency}'"
407                )
408            }
409        }
410    }
411}
412
413impl std::error::Error for ParameterValidationError {}
414
415/// Type-level feature flag validation
416pub trait FeatureFlags {
417    const SUPPORTS_PARALLEL: bool;
418    const SUPPORTS_GPU: bool;
419    const REQUIRES_BLAS: bool;
420    const MEMORY_INTENSIVE: bool;
421}
422
423/// Validated configuration with feature constraints
424pub struct FeatureValidatedConfig<T, F>
425where
426    T: ValidConfig,
427    F: FeatureFlags,
428{
429    config: ValidatedConfig<T>,
430    _features: PhantomData<F>,
431}
432
433impl<T, F> FeatureValidatedConfig<T, F>
434where
435    T: ValidConfig,
436    F: FeatureFlags,
437{
438    /// Create a feature-validated configuration
439    pub fn new(config: T) -> Result<Self, ConfigurationValidationError> {
440        // Validate feature compatibility
441        Self::validate_features()?;
442
443        let validated_config = ValidatedConfig::new(config)
444            .map_err(|_| ConfigurationValidationError::InvalidConfiguration)?;
445
446        Ok(FeatureValidatedConfig {
447            config: validated_config,
448            _features: PhantomData,
449        })
450    }
451
452    /// Validate feature requirements at compile time
453    const fn validate_features() -> Result<(), ConfigurationValidationError> {
454        // This would contain compile-time feature validation logic
455        Ok(())
456    }
457
458    /// Check if parallel execution is supported
459    #[must_use]
460    pub const fn supports_parallel() -> bool {
461        F::SUPPORTS_PARALLEL
462    }
463
464    /// Check if GPU acceleration is supported
465    #[must_use]
466    pub const fn supports_gpu() -> bool {
467        F::SUPPORTS_GPU
468    }
469
470    /// Access the validated configuration
471    pub fn config(&self) -> &ValidatedConfig<T> {
472        &self.config
473    }
474}
475
476/// Enhanced configuration validation errors
477#[derive(Debug, Clone, PartialEq, Eq)]
478pub enum ConfigurationValidationError {
479    /// InvalidConfiguration
480    InvalidConfiguration,
481    /// FeatureNotSupported
482    FeatureNotSupported { feature: String },
483    /// IncompatibleFeatures
484    IncompatibleFeatures { feature1: String, feature2: String },
485    /// ResourceConstraintViolation
486    ResourceConstraintViolation { resource: String, limit: String },
487}
488
489impl fmt::Display for ConfigurationValidationError {
490    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491        match self {
492            ConfigurationValidationError::InvalidConfiguration => {
493                write!(f, "Configuration validation failed")
494            }
495            ConfigurationValidationError::FeatureNotSupported { feature } => {
496                write!(f, "Feature '{feature}' is not supported")
497            }
498            ConfigurationValidationError::IncompatibleFeatures { feature1, feature2 } => {
499                write!(f, "Features '{feature1}' and '{feature2}' are incompatible")
500            }
501            ConfigurationValidationError::ResourceConstraintViolation { resource, limit } => {
502                write!(f, "Resource '{resource}' violates constraint '{limit}'")
503            }
504        }
505    }
506}
507
508impl std::error::Error for ConfigurationValidationError {}
509
510/// Compile-time pipeline stage validation
511pub trait PipelineStage {
512    type Input;
513    type Output;
514    type Config: ValidConfig;
515
516    const STAGE_NAME: &'static str;
517    const IS_TRANSFORMATIVE: bool;
518    const IS_TERMINAL: bool;
519
520    #[must_use]
521    fn validate_compatibility<U: PipelineStage>() -> bool
522    where
523        Self::Output: CompatibleWith<U::Input>,
524    {
525        true
526    }
527}
528
529/// Type-level compatibility checking
530pub trait CompatibleWith<T> {}
531
532// Implement compatibility for common types
533impl CompatibleWith<f64> for f64 {}
534impl CompatibleWith<i32> for i32 {}
535impl<T> CompatibleWith<Vec<T>> for Vec<T> {}
536
537/// Compile-time validated pipeline
538pub struct ValidatedPipeline<Stages> {
539    stages: Stages,
540}
541
542impl<S1> ValidatedPipeline<(S1,)>
543where
544    S1: PipelineStage,
545{
546    /// Create a single-stage validated pipeline
547    pub fn new(stage: S1) -> Self
548    where
549        S1::Config: ValidConfig,
550    {
551        /// ValidatedPipeline
552        ValidatedPipeline { stages: (stage,) }
553    }
554}
555
556impl<S1, S2> ValidatedPipeline<(S1, S2)>
557where
558    S1: PipelineStage,
559    S2: PipelineStage,
560    S1::Output: CompatibleWith<S2::Input>,
561{
562    /// Create a two-stage validated pipeline with compile-time compatibility checking
563    pub fn new(stage1: S1, stage2: S2) -> Self
564    where
565        S1::Config: ValidConfig,
566        S2::Config: ValidConfig,
567    {
568        /// ValidatedPipeline
569        ValidatedPipeline {
570            stages: (stage1, stage2),
571        }
572    }
573}
574
575/// Macro for creating validated configurations with compile-time checks
576#[macro_export]
577macro_rules! validated_config {
578    ($config_type:ty, $($field:ident: $value:expr),*) => {{
579        // This would expand to create a validated configuration
580        // with compile-time validation where possible
581        compile_error!("Macro implementation would go here");
582    }};
583}
584
585/// Advanced type-level validation for complex configurations
586pub struct TypedConfigurationValidator<T, const N: usize> {
587    validators: [fn(&T) -> ValidationResult; N],
588    _phantom: PhantomData<T>,
589}
590
591impl<T, const N: usize> TypedConfigurationValidator<T, N> {
592    /// Create a typed validator with a fixed number of validation functions
593    pub const fn new(validators: [fn(&T) -> ValidationResult; N]) -> Self {
594        Self {
595            validators,
596            _phantom: PhantomData,
597        }
598    }
599
600    /// Validate configuration using all registered validators
601    pub fn validate(&self, config: &T) -> ValidationReport {
602        let mut report = ValidationReport::new();
603
604        for validator in &self.validators {
605            let result = validator(config);
606            report.add_result(result);
607        }
608
609        report
610    }
611}
612
613/// Runtime configuration validator
614pub struct RuntimeValidator {
615    schemas: HashMap<String, ValidationSchema>,
616    custom_validators:
617        HashMap<String, Box<dyn Fn(&dyn std::fmt::Debug) -> ValidationResult + Send + Sync>>,
618}
619
620impl Default for RuntimeValidator {
621    fn default() -> Self {
622        Self::new()
623    }
624}
625
626impl RuntimeValidator {
627    #[must_use]
628    pub fn new() -> Self {
629        Self {
630            schemas: HashMap::new(),
631            custom_validators: HashMap::new(),
632        }
633    }
634
635    pub fn register_schema(&mut self, name: String, schema: ValidationSchema) {
636        self.schemas.insert(name, schema);
637    }
638
639    pub fn register_custom_validator<F>(&mut self, name: String, validator: F)
640    where
641        F: Fn(&dyn std::fmt::Debug) -> ValidationResult + Send + Sync + 'static,
642    {
643        self.custom_validators.insert(name, Box::new(validator));
644    }
645
646    pub fn validate_configuration<T>(&self, config: &T, schema_name: &str) -> ValidationReport
647    where
648        T: fmt::Debug,
649    {
650        let mut report = ValidationReport::new();
651
652        if let Some(schema) = self.schemas.get(schema_name) {
653            // Validate against schema
654            for (field_name, constraints) in &schema.fields {
655                let field_result = self.validate_field_constraints(field_name, constraints, config);
656                if let Some(result) = field_result {
657                    report.add_result(result);
658                }
659            }
660
661            // Validate dependencies
662            for dependency in &schema.dependencies {
663                let dep_result = self.validate_dependency(dependency, config);
664                if let Some(result) = dep_result {
665                    report.add_result(result);
666                }
667            }
668
669            // Apply custom rules
670            for rule in &schema.custom_rules {
671                let rule_result = self.apply_custom_rule(rule, config);
672                if let Some(result) = rule_result {
673                    report.add_result(result);
674                }
675            }
676        } else {
677            report.add_result(
678                ValidationResult::new(
679                    ValidationSeverity::Error,
680                    "RuntimeValidator",
681                    "schema",
682                    format!("Schema '{schema_name}' not found"),
683                )
684                .with_error_code("SCHEMA_NOT_FOUND"),
685            );
686        }
687
688        report
689    }
690
691    fn validate_field_constraints<T>(
692        &self,
693        field_name: &str,
694        constraints: &FieldConstraints,
695        config: &T,
696    ) -> Option<ValidationResult>
697    where
698        T: fmt::Debug,
699    {
700        // Simplified validation - in practice this would inspect the actual field values
701        if constraints.required {
702            // Check if required field is present and valid
703            None // Placeholder - would contain actual validation logic
704        } else {
705            None
706        }
707    }
708
709    fn validate_dependency<T>(
710        &self,
711        dependency: &DependencyConstraint,
712        config: &T,
713    ) -> Option<ValidationResult>
714    where
715        T: fmt::Debug,
716    {
717        // Evaluate dependency condition
718        // Placeholder - would contain actual dependency checking logic
719        None
720    }
721
722    fn apply_custom_rule<T>(
723        &self,
724        rule: &CustomValidationRule,
725        config: &T,
726    ) -> Option<ValidationResult>
727    where
728        T: fmt::Debug,
729    {
730        // Apply custom validation rule
731        // Placeholder - would contain actual custom rule logic
732        None
733    }
734}
735
736/// Pipeline-specific configuration validator
737pub struct PipelineConfigValidator {
738    validator: RuntimeValidator,
739}
740
741impl Default for PipelineConfigValidator {
742    fn default() -> Self {
743        Self::new()
744    }
745}
746
747impl PipelineConfigValidator {
748    #[must_use]
749    pub fn new() -> Self {
750        let mut validator = RuntimeValidator::new();
751
752        // Register pipeline-specific schemas
753        let pipeline_schema = ValidationSchema {
754            name: "PipelineConfig".to_string(),
755            version: "1.0.0".to_string(),
756            fields: Self::create_pipeline_field_constraints(),
757            dependencies: Self::create_pipeline_dependencies(),
758            custom_rules: Self::create_pipeline_custom_rules(),
759        };
760
761        validator.register_schema("PipelineConfig".to_string(), pipeline_schema);
762
763        // Register custom validators
764        validator.register_custom_validator("performance_check".to_string(), |config| {
765            ValidationResult::new(
766                ValidationSeverity::Info,
767                "PipelineConfig",
768                "performance",
769                "Performance validation passed",
770            )
771        });
772
773        Self { validator }
774    }
775
776    fn create_pipeline_field_constraints() -> HashMap<String, FieldConstraints> {
777        let mut fields = HashMap::new();
778
779        fields.insert(
780            "n_jobs".to_string(),
781            /// FieldConstraints
782            FieldConstraints {
783                required: false,
784                field_type: FieldType::Integer,
785                constraints: vec![Constraint::Range {
786                    min: -1.0,
787                    max: 1000.0,
788                }],
789                description: "Number of parallel jobs (-1 for all cores)".to_string(),
790                examples: vec!["1".to_string(), "4".to_string(), "-1".to_string()],
791            },
792        );
793
794        fields.insert(
795            "random_state".to_string(),
796            /// FieldConstraints
797            FieldConstraints {
798                required: false,
799                field_type: FieldType::Integer,
800                constraints: vec![Constraint::Range {
801                    min: 0.0,
802                    max: 2_147_483_647.0,
803                }],
804                description: "Random seed for reproducibility".to_string(),
805                examples: vec!["42".to_string(), "123".to_string()],
806            },
807        );
808
809        fields.insert(
810            "verbose".to_string(),
811            /// FieldConstraints
812            FieldConstraints {
813                required: false,
814                field_type: FieldType::Boolean,
815                constraints: vec![],
816                description: "Enable verbose output".to_string(),
817                examples: vec!["true".to_string(), "false".to_string()],
818            },
819        );
820
821        fields
822    }
823
824    fn create_pipeline_dependencies() -> Vec<DependencyConstraint> {
825        vec![DependencyConstraint {
826            name: "parallel_processing_compatibility".to_string(),
827            condition: "n_jobs > 1 implies thread_safe == true".to_string(),
828            error_message: "Parallel processing requires thread-safe components".to_string(),
829            severity: ValidationSeverity::Error,
830        }]
831    }
832
833    fn create_pipeline_custom_rules() -> Vec<CustomValidationRule> {
834        vec![CustomValidationRule {
835            name: "memory_usage_warning".to_string(),
836            description: "Warn about potentially high memory usage".to_string(),
837            rule_type: RuleType::PerformanceWarning,
838            parameters: HashMap::new(),
839        }]
840    }
841
842    pub fn validate_pipeline_config<T>(&self, config: &T) -> ValidationReport
843    where
844        T: fmt::Debug,
845    {
846        self.validator
847            .validate_configuration(config, "PipelineConfig")
848    }
849}
850
851/// Configuration validation builder for fluent API
852pub struct ValidationBuilder {
853    schema: ValidationSchema,
854}
855
856impl ValidationBuilder {
857    pub fn new(name: impl Into<String>) -> Self {
858        Self {
859            schema: ValidationSchema {
860                name: name.into(),
861                version: "1.0.0".to_string(),
862                fields: HashMap::new(),
863                dependencies: Vec::new(),
864                custom_rules: Vec::new(),
865            },
866        }
867    }
868
869    pub fn add_field(mut self, name: impl Into<String>, constraints: FieldConstraints) -> Self {
870        self.schema.fields.insert(name.into(), constraints);
871        self
872    }
873
874    #[must_use]
875    pub fn add_dependency(mut self, dependency: DependencyConstraint) -> Self {
876        self.schema.dependencies.push(dependency);
877        self
878    }
879
880    #[must_use]
881    pub fn add_custom_rule(mut self, rule: CustomValidationRule) -> Self {
882        self.schema.custom_rules.push(rule);
883        self
884    }
885
886    #[must_use]
887    pub fn build(self) -> ValidationSchema {
888        self.schema
889    }
890}
891
892/// Enhanced configuration validation examples
893pub mod examples {
894    use super::{
895        Constraint, CustomValidationRule, DependencyConstraint, FieldConstraints, FieldType,
896        HashMap, RuleType, ValidationBuilder, ValidationSchema, ValidationSeverity,
897    };
898
899    #[must_use]
900    pub fn create_linear_regression_validator() -> ValidationSchema {
901        ValidationBuilder::new("LinearRegressionConfig")
902            .add_field(
903                "fit_intercept",
904                /// FieldConstraints
905                FieldConstraints {
906                    required: false,
907                    field_type: FieldType::Boolean,
908                    constraints: vec![],
909                    description: "Whether to calculate the intercept".to_string(),
910                    examples: vec!["true".to_string(), "false".to_string()],
911                },
912            )
913            .add_field(
914                "alpha",
915                /// FieldConstraints
916                FieldConstraints {
917                    required: false,
918                    field_type: FieldType::Float,
919                    constraints: vec![Constraint::Range { min: 0.0, max: 1e6 }],
920                    description: "Regularization strength".to_string(),
921                    examples: vec!["0.01".to_string(), "1.0".to_string(), "100.0".to_string()],
922                },
923            )
924            .add_dependency(DependencyConstraint {
925                name: "regularization_warning".to_string(),
926                condition: "alpha > 1000".to_string(),
927                error_message: "Very high regularization may lead to underfitting".to_string(),
928                severity: ValidationSeverity::Warning,
929            })
930            .build()
931    }
932
933    #[must_use]
934    pub fn create_ensemble_validator() -> ValidationSchema {
935        ValidationBuilder::new("EnsembleConfig")
936            .add_field(
937                "n_estimators",
938                /// FieldConstraints
939                FieldConstraints {
940                    required: true,
941                    field_type: FieldType::Integer,
942                    constraints: vec![Constraint::Range {
943                        min: 1.0,
944                        max: 10000.0,
945                    }],
946                    description: "Number of estimators in the ensemble".to_string(),
947                    examples: vec!["10".to_string(), "100".to_string(), "1000".to_string()],
948                },
949            )
950            .add_field(
951                "voting",
952                /// FieldConstraints
953                FieldConstraints {
954                    required: false,
955                    field_type: FieldType::Enum(vec!["hard".to_string(), "soft".to_string()]),
956                    constraints: vec![Constraint::OneOf(vec![
957                        "hard".to_string(),
958                        "soft".to_string(),
959                    ])],
960                    description: "Voting strategy for ensemble".to_string(),
961                    examples: vec!["hard".to_string(), "soft".to_string()],
962                },
963            )
964            .add_custom_rule(CustomValidationRule {
965                name: "performance_vs_accuracy_tradeoff".to_string(),
966                description: "Warn about performance implications of large ensembles".to_string(),
967                rule_type: RuleType::PerformanceWarning,
968                parameters: HashMap::new(),
969            })
970            .build()
971    }
972}
973
974#[allow(non_snake_case)]
975#[cfg(test)]
976mod tests {
977    use super::*;
978
979    #[test]
980    fn test_validation_result_creation() {
981        let result = ValidationResult::new(
982            ValidationSeverity::Warning,
983            "TestComponent",
984            "test_field",
985            "Test message",
986        )
987        .with_suggestions(vec!["Fix suggestion".to_string()])
988        .with_error_code("TEST_001");
989
990        assert_eq!(result.severity, ValidationSeverity::Warning);
991        assert_eq!(result.component, "TestComponent");
992        assert_eq!(result.field, "test_field");
993        assert_eq!(result.message, "Test message");
994        assert_eq!(result.suggestions.len(), 1);
995        assert_eq!(result.error_code, "TEST_001");
996        assert!(!result.is_error());
997    }
998
999    #[test]
1000    fn test_validation_report() {
1001        let mut report = ValidationReport::new();
1002
1003        report.add_result(ValidationResult::new(
1004            ValidationSeverity::Warning,
1005            "Component1",
1006            "field1",
1007            "Warning message",
1008        ));
1009
1010        report.add_result(ValidationResult::new(
1011            ValidationSeverity::Error,
1012            "Component2",
1013            "field2",
1014            "Error message",
1015        ));
1016
1017        assert_eq!(report.summary.warnings, 1);
1018        assert_eq!(report.summary.errors, 1);
1019        assert_eq!(report.summary.overall_status, ValidationStatus::Failed);
1020        assert!(report.has_errors());
1021    }
1022
1023    #[test]
1024    fn test_pipeline_config_validator() {
1025        let validator = PipelineConfigValidator::new();
1026
1027        // Mock configuration for testing
1028        #[derive(Debug)]
1029        struct MockConfig {
1030            n_jobs: i32,
1031            verbose: bool,
1032        }
1033
1034        let config = MockConfig {
1035            n_jobs: 4,
1036            verbose: true,
1037        };
1038
1039        let report = validator.validate_pipeline_config(&config);
1040
1041        // In a real implementation, this would have actual validation results
1042        assert_eq!(report.summary.overall_status, ValidationStatus::Passed);
1043    }
1044
1045    #[test]
1046    fn test_validation_builder() {
1047        let schema = ValidationBuilder::new("TestSchema")
1048            .add_field(
1049                "test_field",
1050                /// FieldConstraints
1051                FieldConstraints {
1052                    required: true,
1053                    field_type: FieldType::Integer,
1054                    constraints: vec![Constraint::Range {
1055                        min: 1.0,
1056                        max: 100.0,
1057                    }],
1058                    description: "Test field".to_string(),
1059                    examples: vec!["50".to_string()],
1060                },
1061            )
1062            .build();
1063
1064        assert_eq!(schema.name, "TestSchema");
1065        assert_eq!(schema.fields.len(), 1);
1066        assert!(schema.fields.contains_key("test_field"));
1067    }
1068
1069    #[test]
1070    fn test_validated_parameter() {
1071        // Test compile-time validation with const generics
1072        type JobCount = ValidatedParameter<1, 8>;
1073
1074        // Valid parameter
1075        let valid_param = JobCount::new_runtime(4).unwrap();
1076        assert_eq!(valid_param.value(), 4);
1077
1078        // Invalid parameter
1079        let invalid_param = JobCount::new_runtime(10);
1080        assert!(invalid_param.is_err());
1081
1082        match invalid_param.unwrap_err() {
1083            ParameterValidationError::OutOfRange { value, min, max } => {
1084                assert_eq!(value, 10);
1085                assert_eq!(min, 1);
1086                assert_eq!(max, 8);
1087            }
1088            _ => panic!("Expected OutOfRange error"),
1089        }
1090    }
1091
1092    #[test]
1093    fn test_feature_flags() {
1094        // Mock feature flags for testing
1095        struct TestFeatures;
1096        impl FeatureFlags for TestFeatures {
1097            const SUPPORTS_PARALLEL: bool = true;
1098            const SUPPORTS_GPU: bool = false;
1099            const REQUIRES_BLAS: bool = true;
1100            const MEMORY_INTENSIVE: bool = false;
1101        }
1102
1103        // Mock valid configuration
1104        #[derive(Debug)]
1105        struct TestConfig;
1106        impl ValidConfig for TestConfig {
1107            type Error = ConfigurationValidationError;
1108            const IS_VALID: bool = true;
1109
1110            fn validate_config() -> Result<(), Self::Error> {
1111                Ok(())
1112            }
1113        }
1114
1115        // Test feature validation
1116        let config = TestConfig;
1117        let feature_config = FeatureValidatedConfig::<TestConfig, TestFeatures>::new(config);
1118        assert!(feature_config.is_ok());
1119
1120        // Test feature queries
1121        assert!(FeatureValidatedConfig::<TestConfig, TestFeatures>::supports_parallel());
1122        assert!(!FeatureValidatedConfig::<TestConfig, TestFeatures>::supports_gpu());
1123    }
1124
1125    #[test]
1126    fn test_pipeline_stage_compatibility() {
1127        // Mock pipeline stages for testing
1128        #[derive(Debug)]
1129        struct TestStage1;
1130        #[derive(Debug)]
1131        struct TestStage2;
1132
1133        #[derive(Debug)]
1134        struct TestConfig;
1135        impl ValidConfig for TestConfig {
1136            type Error = ConfigurationValidationError;
1137            const IS_VALID: bool = true;
1138
1139            fn validate_config() -> Result<(), Self::Error> {
1140                Ok(())
1141            }
1142        }
1143
1144        impl PipelineStage for TestStage1 {
1145            type Input = i32;
1146            type Output = f64;
1147            type Config = TestConfig;
1148
1149            const STAGE_NAME: &'static str = "TestStage1";
1150            const IS_TRANSFORMATIVE: bool = true;
1151            const IS_TERMINAL: bool = false;
1152        }
1153
1154        impl PipelineStage for TestStage2 {
1155            type Input = f64;
1156            type Output = String;
1157            type Config = TestConfig;
1158
1159            const STAGE_NAME: &'static str = "TestStage2";
1160            const IS_TRANSFORMATIVE: bool = true;
1161            const IS_TERMINAL: bool = true;
1162        }
1163
1164        // Test pipeline creation with compatible stages
1165        let stage1 = TestStage1;
1166        let stage2 = TestStage2;
1167        let _pipeline = ValidatedPipeline::<(TestStage1, TestStage2)>::new(stage1, stage2);
1168
1169        // Test stage properties
1170        assert_eq!(TestStage1::STAGE_NAME, "TestStage1");
1171        assert!(TestStage1::IS_TRANSFORMATIVE);
1172        assert!(!TestStage1::IS_TERMINAL);
1173        assert!(TestStage2::IS_TERMINAL);
1174    }
1175
1176    #[test]
1177    fn test_typed_configuration_validator() {
1178        // Test configuration type
1179        #[derive(Debug)]
1180        struct TestConfig {
1181            value: i32,
1182        }
1183
1184        // Validation functions
1185        fn validate_positive(config: &TestConfig) -> ValidationResult {
1186            if config.value > 0 {
1187                ValidationResult::new(
1188                    ValidationSeverity::Info,
1189                    "TestConfig",
1190                    "value",
1191                    "Value is positive",
1192                )
1193            } else {
1194                ValidationResult::new(
1195                    ValidationSeverity::Error,
1196                    "TestConfig",
1197                    "value",
1198                    "Value must be positive",
1199                )
1200            }
1201        }
1202
1203        fn validate_range(config: &TestConfig) -> ValidationResult {
1204            if config.value >= 1 && config.value <= 100 {
1205                ValidationResult::new(
1206                    ValidationSeverity::Info,
1207                    "TestConfig",
1208                    "value",
1209                    "Value is in range",
1210                )
1211            } else {
1212                ValidationResult::new(
1213                    ValidationSeverity::Warning,
1214                    "TestConfig",
1215                    "value",
1216                    "Value is outside recommended range",
1217                )
1218            }
1219        }
1220
1221        // Create typed validator with const generic size
1222        let validator = TypedConfigurationValidator::new([validate_positive, validate_range]);
1223
1224        // Test with valid configuration
1225        let valid_config = TestConfig { value: 50 };
1226        let report = validator.validate(&valid_config);
1227        assert_eq!(report.summary.errors, 0);
1228
1229        // Test with invalid configuration
1230        let invalid_config = TestConfig { value: -10 };
1231        let report = validator.validate(&invalid_config);
1232        assert_eq!(report.summary.errors, 1);
1233        assert_eq!(report.summary.warnings, 1);
1234    }
1235
1236    #[test]
1237    fn test_parameter_validation_error_display() {
1238        let error = ParameterValidationError::OutOfRange {
1239            value: 15,
1240            min: 1,
1241            max: 10,
1242        };
1243        assert_eq!(
1244            error.to_string(),
1245            "Parameter value 15 is out of range [1, 10]"
1246        );
1247
1248        let error = ParameterValidationError::InvalidType {
1249            expected: "Integer".to_string(),
1250            actual: "String".to_string(),
1251        };
1252        assert_eq!(error.to_string(), "Expected type 'Integer', found 'String'");
1253
1254        let error = ParameterValidationError::MissingRequired {
1255            parameter: "alpha".to_string(),
1256        };
1257        assert_eq!(error.to_string(), "Required parameter 'alpha' is missing");
1258    }
1259
1260    #[test]
1261    fn test_configuration_validation_error_display() {
1262        let error = ConfigurationValidationError::FeatureNotSupported {
1263            feature: "GPU".to_string(),
1264        };
1265        assert_eq!(error.to_string(), "Feature 'GPU' is not supported");
1266
1267        let error = ConfigurationValidationError::IncompatibleFeatures {
1268            feature1: "Parallel".to_string(),
1269            feature2: "SingleThread".to_string(),
1270        };
1271        assert_eq!(
1272            error.to_string(),
1273            "Features 'Parallel' and 'SingleThread' are incompatible"
1274        );
1275    }
1276}