scirs2_core/validation/data/
validator.rs

1//! Main validator implementation
2//!
3//! This module provides the core `Validator` struct that orchestrates all validation
4//! operations and manages caching, custom rules, and configuration.
5//!
6//! ## Features
7//!
8//! - **Schema-based validation**: Validate data against predefined schemas
9//! - **Custom validation rules**: Extend validation with custom business logic
10//! - **Caching**: Improve performance with result caching
11//! - **Array validation**: Specialized validation for scientific arrays
12//! - **Quality analysis**: Generate comprehensive data quality reports
13//!
14//! ## Examples
15//!
16//! ### Basic validation
17//!
18//! ```rust
19//! use scirs2_core::validation::data::{Validator, ValidationConfig, ValidationSchema, DataType};
20//!
21//! let config = ValidationConfig::default();
22//! let validator = Validator::new(config)?;
23//!
24//! let schema = ValidationSchema::new()
25//!     .name("user_schema")
26//!     .require_field("name", DataType::String)
27//!     .require_field("age", DataType::Integer);
28//!
29//! #
30//! # {
31//! let data = serde_json::json!({
32//!     "name": "John Doe",
33//!     "age": 30
34//! });
35//!
36//! let result = validator.validate(&data, &schema)?;
37//! assert!(result.is_valid());
38//! # }
39//! # Ok::<(), Box<dyn std::error::Error>>(())
40//! ```
41//!
42//! ### Array validation
43//!
44//! ```rust
45//! use scirs2_core::validation::data::{Validator, ValidationConfig, ArrayValidationConstraints};
46//! use ::ndarray::Array2;
47//!
48//! let config = ValidationConfig::default();
49//! let validator = Validator::new(config.clone())?;
50//!
51//! let data = Array2::from_shape_vec((3, 2), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])?;
52//!
53//! let constraints = ArrayValidationConstraints::new()
54//!     .withshape(vec![3, 2])
55//!     .check_numeric_quality();
56//!
57//! let result = validator.validate_ndarray(&data, &constraints, &config)?;
58//! assert!(result.is_valid());
59//! # Ok::<(), Box<dyn std::error::Error>>(())
60//! ```
61
62use crate::error::{CoreError, ErrorContext};
63use std::collections::{HashMap, HashSet};
64use std::sync::{Arc, RwLock};
65use std::time::Instant;
66
67use super::array_validation::ArrayValidator;
68use super::config::{ErrorSeverity, ValidationConfig, ValidationErrorType};
69use super::constraints::{ArrayValidationConstraints, Constraint};
70use super::errors::{ValidationError, ValidationResult, ValidationStats};
71use super::quality::{DataQualityReport, QualityAnalyzer};
72use super::schema::{DataType, ValidationSchema};
73
74// Core dependencies for array/matrix validation
75use ::ndarray::{ArrayBase, Data, Dimension, ScalarOperand};
76use num_traits::{Float, FromPrimitive};
77use std::fmt;
78
79use serde_json::Value as JsonValue;
80
81use std::collections::hash_map::DefaultHasher;
82use std::hash::{Hash, Hasher};
83
84/// Cache entry for validation results
85#[derive(Debug, Clone)]
86struct CacheEntry {
87    result: ValidationResult,
88    timestamp: Instant,
89    hit_count: usize,
90}
91
92/// Trait for custom validation rules
93pub trait ValidationRule {
94    /// Validate a value
95
96    fn validate(&self, value: &JsonValue, fieldpath: &str) -> Result<(), String>;
97
98    /// Get rule name
99    fn name(&self) -> &str;
100
101    /// Get rule description
102    fn description(&self) -> &str;
103}
104
105/// Main data validator with comprehensive validation capabilities
106pub struct Validator {
107    /// Validation configuration
108    config: ValidationConfig,
109    /// Validation result cache
110    cache: Arc<RwLock<HashMap<String, CacheEntry>>>,
111    /// Custom validation rules
112    custom_rules: HashMap<String, Box<dyn ValidationRule + Send + Sync>>,
113    /// Array validator
114    array_validator: ArrayValidator,
115    /// Quality analyzer
116    quality_analyzer: QualityAnalyzer,
117}
118
119impl Validator {
120    /// Create a new validator with configuration
121    ///
122    /// # Arguments
123    ///
124    /// * `config` - Validation configuration settings
125    ///
126    /// # Returns
127    ///
128    /// A new `Validator` instance or an error if initialization fails
129    ///
130    /// # Example
131    ///
132    /// ```rust
133    /// use scirs2_core::validation::data::{Validator, ValidationConfig};
134    ///
135    /// let mut config = ValidationConfig::default();
136    /// config.max_depth = 10;
137    /// config.strict_mode = true;
138    ///
139    /// let validator = Validator::new(config)?;
140    /// # Ok::<(), Box<dyn std::error::Error>>(())
141    /// ```
142    pub fn new(config: ValidationConfig) -> Result<Self, CoreError> {
143        Ok(Self {
144            config,
145            cache: Arc::new(RwLock::new(HashMap::new())),
146            custom_rules: HashMap::new(),
147            array_validator: ArrayValidator::new(),
148            quality_analyzer: QualityAnalyzer::new(),
149        })
150    }
151
152    /// Validate JSON data against a schema
153    ///
154    /// This method performs comprehensive validation of JSON data against a predefined schema,
155    /// including type checking, constraint validation, and custom rules.
156    ///
157    /// # Arguments
158    ///
159    /// * `data` - The JSON data to validate
160    /// * `schema` - The validation schema to apply
161    ///
162    /// # Returns
163    ///
164    /// A `ValidationResult` containing the validation outcome and any errors/warnings
165    ///
166    /// # Example
167    ///
168    /// ```rust
169    /// #
170    /// # {
171    /// use scirs2_core::validation::data::{Validator, ValidationSchema, DataType, Constraint, ValidationConfig};
172    ///
173    /// let validator = Validator::new(ValidationConfig::default())?;
174    ///
175    /// let schema = ValidationSchema::new()
176    ///     .require_field("email", DataType::String)
177    ///     .add_constraint("email", Constraint::Pattern("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$".to_string()));
178    ///
179    /// let data = serde_json::json!({
180    ///     "email": "user@example.com"
181    /// });
182    ///
183    /// let result = validator.validate(&data, &schema)?;
184    /// assert!(result.is_valid());
185    /// # }
186    /// # Ok::<(), Box<dyn std::error::Error>>(())
187    /// ```
188
189    pub fn validate(
190        &self,
191        data: &JsonValue,
192        schema: &ValidationSchema,
193    ) -> Result<ValidationResult, CoreError> {
194        let start_time = Instant::now();
195        let mut errors = Vec::new();
196        let mut warnings = Vec::new();
197        let mut stats = ValidationStats::default();
198
199        // Check cache if enabled
200        if self.config.enable_caching {
201            let cachekey = self.generate_cachekey(data, schema)?;
202            if let Some(mut cached_result) = self.get_cached_result(&cachekey)? {
203                // Update cache hit rate
204                let cache_hit_rate = self.calculate_cache_hit_rate()?;
205                cached_result.stats.set_cache_hit_rate(cache_hit_rate);
206                return Ok(cached_result);
207            }
208        }
209
210        // Validate each field in the schema
211        self.validate_fields(data, schema, "", &mut errors, &mut warnings, &mut stats, 0)?;
212
213        // Apply global constraints
214        self.validate_global_constraints(data, schema, &mut errors, &mut warnings, &mut stats)?;
215
216        // Check for additional fields if not allowed
217        if !schema.allow_additional_fields {
218            self.check_additional_fields(data, schema, &mut errors, &mut warnings)?;
219        }
220
221        let valid = errors.is_empty()
222            && !warnings
223                .iter()
224                .any(|w| w.severity == ErrorSeverity::Critical);
225        let duration = start_time.elapsed();
226
227        let mut result = ValidationResult {
228            valid,
229            errors,
230            warnings,
231            stats,
232            duration,
233        };
234
235        // Cache result if enabled
236        if self.config.enable_caching {
237            let cachekey = self.generate_cachekey(data, schema)?;
238            self.cache_result(&cachekey, result.clone())?;
239        }
240
241        // Update cache hit rate
242        if self.config.enable_caching {
243            let cache_hit_rate = self.calculate_cache_hit_rate()?;
244            result.stats.set_cache_hit_rate(cache_hit_rate);
245        }
246
247        Ok(result)
248    }
249
250    /// Validate ndarray with comprehensive checks
251    ///
252    /// Performs validation on scientific arrays including shape validation, numeric quality
253    /// checks, statistical constraints, and performance characteristics.
254    ///
255    /// # Arguments
256    ///
257    /// * `array` - The ndarray to validate
258    /// * `constraints` - Validation constraints to apply
259    /// * `config` - Validation configuration
260    ///
261    /// # Type Parameters
262    ///
263    /// * `S` - Storage type (must implement `Data`)
264    /// * `D` - Dimension type
265    /// * `S::Elem` - Element type (must be a floating-point type)
266    ///
267    /// # Returns
268    ///
269    /// A `ValidationResult` with detailed validation information
270    ///
271    /// # Example
272    ///
273    /// ```rust
274    /// use scirs2_core::validation::data::{Validator, ValidationConfig, ArrayValidationConstraints};
275    /// use ::ndarray::Array2;
276    ///
277    /// let validator = Validator::new(ValidationConfig::default())?;
278    ///
279    /// let data = Array2::from_shape_vec((3, 3), vec![
280    ///     1.0, 2.0, 3.0,
281    ///     4.0, 5.0, 6.0,
282    ///     7.0, 8.0, 9.0
283    /// ])?;
284    ///
285    /// let constraints = ArrayValidationConstraints::new()
286    ///     .withshape(vec![3, 3])
287    ///     .check_numeric_quality();
288    ///
289    /// let result = validator.validate_ndarray(&data, &constraints, &ValidationConfig::default())?;
290    /// assert!(result.is_valid());
291    /// # Ok::<(), Box<dyn std::error::Error>>(())
292    /// ```
293    pub fn validate_ndarray<S, D>(
294        &self,
295        array: &ArrayBase<S, D>,
296        constraints: &ArrayValidationConstraints,
297        config: &ValidationConfig,
298    ) -> Result<ValidationResult, CoreError>
299    where
300        S: Data,
301        D: Dimension,
302        S::Elem: Float + fmt::Debug + Send + Sync + ScalarOperand + FromPrimitive,
303    {
304        self.array_validator
305            .validate_ndarray(array, constraints, config)
306    }
307
308    /// Generate comprehensive data quality report
309    ///
310    /// Analyzes an array and generates a detailed quality report including completeness,
311    /// accuracy, consistency, and statistical properties.
312    ///
313    /// # Arguments
314    ///
315    /// * `array` - The array to analyze
316    /// * `fieldname` - Name of the field for reporting
317    ///
318    /// # Returns
319    ///
320    /// A `DataQualityReport` with quality metrics and recommendations
321    ///
322    /// # Example
323    ///
324    /// ```rust
325    /// use scirs2_core::validation::data::{Validator, ValidationConfig};
326    /// use ::ndarray::Array1;
327    ///
328    /// let validator = Validator::new(ValidationConfig::default())?;
329    ///
330    /// let data = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
331    /// let report = validator.generate_quality_report(&data, "measurements")?;
332    ///
333    /// println!("Quality score: {}", report.quality_score);
334    /// println!("Completeness: {}", report.metrics.completeness);
335    /// # Ok::<(), Box<dyn std::error::Error>>(())
336    /// ```
337    pub fn generate_quality_report<S, D>(
338        &self,
339        array: &ArrayBase<S, D>,
340        fieldname: &str,
341    ) -> Result<DataQualityReport, CoreError>
342    where
343        S: Data,
344        D: Dimension,
345        S::Elem: Float + fmt::Debug + ScalarOperand + Send + Sync + FromPrimitive,
346    {
347        self.quality_analyzer
348            .generate_quality_report(array, fieldname)
349    }
350
351    /// Add a custom validation rule
352    ///
353    /// Registers a custom validation rule that can be referenced in schemas.
354    ///
355    /// # Arguments
356    ///
357    /// * `name` - Unique name for the rule
358    /// * `rule` - The validation rule implementation
359    ///
360    /// # Example
361    ///
362    /// ```rust
363    /// use scirs2_core::validation::data::{Validator, ValidationConfig, ValidationRule};
364    /// use serde_json::Value as JsonValue;
365    ///
366    /// struct EmailRule;
367    ///
368    /// impl ValidationRule for EmailRule {
369    ///     fn validate(&self, value: &JsonValue, fieldpath: &str) -> Result<(), String> {
370    ///         if let Some(email) = value.as_str() {
371    ///             if email.contains('@') {
372    ///                 Ok(())
373    ///             } else {
374    ///                 Err(format!("{fieldpath}: invalid email format"))
375    ///             }
376    ///         } else {
377    ///             Err(format!("{fieldpath}: expected string"))
378    ///         }
379    ///     }
380    ///
381    ///     fn name(&self) -> &str { "email" }
382    ///     fn description(&self) -> &str { "Validates email format" }
383    /// }
384    ///
385    /// let mut validator = Validator::new(ValidationConfig::default())?;
386    /// validator.add_custom_rule("email".to_string(), Box::new(EmailRule));
387    /// # Ok::<(), Box<dyn std::error::Error>>(())
388    /// ```
389    pub fn add_custom_rule(&mut self, name: String, rule: Box<dyn ValidationRule + Send + Sync>) {
390        self.custom_rules.insert(name, rule);
391    }
392
393    /// Clear validation cache
394    pub fn clear_cache(&self) -> Result<(), CoreError> {
395        let mut cache = self.cache.write().map_err(|_| {
396            CoreError::ComputationError(ErrorContext::new(
397                "Failed to acquire cache write lock".to_string(),
398            ))
399        })?;
400        cache.clear();
401        Ok(())
402    }
403
404    /// Get cache statistics
405    pub fn get_cache_stats(&self) -> Result<(usize, f64), CoreError> {
406        let cache = self.cache.read().map_err(|_| {
407            CoreError::ComputationError(ErrorContext::new(
408                "Failed to acquire cache read lock".to_string(),
409            ))
410        })?;
411
412        let size = cache.len();
413        let hit_rate = self.calculate_cache_hit_rate()?;
414
415        Ok((size, hit_rate))
416    }
417
418    /// Validate individual fields
419    fn validate_fields(
420        &self,
421        data: &JsonValue,
422        schema: &ValidationSchema,
423        fieldpath: &str,
424        errors: &mut Vec<ValidationError>,
425        warnings: &mut Vec<ValidationError>,
426        stats: &mut ValidationStats,
427        depth: usize,
428    ) -> Result<(), CoreError> {
429        if depth > self.config.max_depth {
430            errors.push(ValidationError {
431                errortype: ValidationErrorType::SchemaError,
432                fieldpath: fieldpath.to_string(),
433                message: "Maximum validation _depth exceeded".to_string(),
434                expected: None,
435                actual: None,
436                constraint: None,
437                severity: ErrorSeverity::Error,
438                context: HashMap::new(),
439            });
440            return Ok(());
441        }
442
443        let data_obj = match data {
444            JsonValue::Object(obj) => obj,
445            _ => {
446                errors.push(ValidationError {
447                    errortype: ValidationErrorType::TypeMismatch,
448                    fieldpath: fieldpath.to_string(),
449                    message: "Expected object".to_string(),
450                    expected: Some("object".to_string()),
451                    actual: Some(self.get_value_type_name(data)),
452                    constraint: None,
453                    severity: ErrorSeverity::Error,
454                    context: HashMap::new(),
455                });
456                return Ok(());
457            }
458        };
459
460        for (fieldname, field_def) in &schema.fields {
461            stats.add_field_validation();
462
463            let fieldpath = if depth == 0 {
464                fieldname.clone()
465            } else {
466                fieldname.to_string()
467            };
468
469            if let Some(field_value) = data_obj.get(fieldname) {
470                // Field exists, validate type and constraints
471                self.validate_field_type(field_value, &field_def.datatype, &fieldpath, errors)?;
472                self.validate_field_constraints(
473                    field_value,
474                    &field_def.constraints,
475                    &fieldpath,
476                    errors,
477                    warnings,
478                    stats,
479                )?;
480
481                // Validate custom rules
482                for rule_name in &field_def.validation_rules {
483                    if let Some(rule) = self.custom_rules.get(rule_name) {
484                        if let Err(ruleerror) = rule.validate(field_value, &fieldpath) {
485                            errors.push(ValidationError {
486                                errortype: ValidationErrorType::CustomRuleFailure,
487                                fieldpath: fieldpath.clone(),
488                                message: ruleerror,
489                                expected: None,
490                                actual: None,
491                                constraint: Some(rule_name.clone()),
492                                severity: ErrorSeverity::Error,
493                                context: HashMap::new(),
494                            });
495                        }
496                    }
497                }
498            } else if field_def.required {
499                // Required field is missing
500                errors.push(ValidationError {
501                    errortype: ValidationErrorType::MissingRequiredField,
502                    fieldpath,
503                    message: format!("Required field '{}' is missing", fieldname),
504                    expected: Some(format!("{:?}", field_def.datatype)),
505                    actual: Some("missing".to_string()),
506                    constraint: Some("required".to_string()),
507                    severity: ErrorSeverity::Error,
508                    context: HashMap::new(),
509                });
510            }
511        }
512
513        Ok(())
514    }
515
516    /// Validate field type
517    fn validate_field_type(
518        &self,
519        value: &JsonValue,
520        expected_type: &DataType,
521        fieldpath: &str,
522        errors: &mut Vec<ValidationError>,
523    ) -> Result<(), CoreError> {
524        let type_matches = match expected_type {
525            DataType::Boolean => value.is_boolean(),
526            DataType::Integer => value.is_i64(),
527            DataType::Float32 | DataType::Float64 => value.is_f64() || value.is_i64(),
528            DataType::String => value.is_string(),
529            DataType::Array(_) => value.is_array(),
530            DataType::Object => value.is_object(),
531            DataType::Null => value.is_null(),
532            _ => true, // Other types not yet implemented
533        };
534
535        if !type_matches {
536            errors.push(ValidationError {
537                errortype: ValidationErrorType::TypeMismatch,
538                fieldpath: fieldpath.to_string(),
539                message: format!(
540                    "Type mismatch: expected {:?}, got {}",
541                    expected_type,
542                    self.get_value_type_name(value)
543                ),
544                expected: Some(format!("{expected_type:?}")),
545                actual: Some(self.get_value_type_name(value)),
546                constraint: Some("type".to_string()),
547                severity: ErrorSeverity::Error,
548                context: HashMap::new(),
549            });
550        }
551
552        Ok(())
553    }
554
555    /// Validate field constraints
556    #[allow(clippy::only_used_in_recursion)]
557    fn validate_field_constraints(
558        &self,
559        value: &JsonValue,
560        constraints: &[Constraint],
561        fieldpath: &str,
562        errors: &mut Vec<ValidationError>,
563        warnings: &mut Vec<ValidationError>,
564        stats: &mut ValidationStats,
565    ) -> Result<(), CoreError> {
566        for constraint in constraints {
567            stats.add_constraint_check();
568
569            match constraint {
570                Constraint::Range { min, max } => {
571                    if let Some(num) = value.as_f64() {
572                        if num < *min || num > *max {
573                            errors.push(ValidationError {
574                                errortype: ValidationErrorType::OutOfRange,
575                                fieldpath: fieldpath.to_string(),
576                                message: format!(
577                                    "Value {} is out of range [{}, {}]",
578                                    num, min, max
579                                ),
580                                expected: Some(format!("[{}, {}]", min, max)),
581                                actual: Some(num.to_string()),
582                                constraint: Some("range".to_string()),
583                                severity: ErrorSeverity::Error,
584                                context: HashMap::new(),
585                            });
586                        }
587                    }
588                }
589                Constraint::Length { min, max } => {
590                    if let Some(s) = value.as_str() {
591                        let len = s.len();
592                        if len < *min || len > *max {
593                            errors.push(ValidationError {
594                                errortype: ValidationErrorType::ConstraintViolation,
595                                fieldpath: fieldpath.to_string(),
596                                message: format!(
597                                    "String length {} is out of range [{}, {}]",
598                                    len, min, max
599                                ),
600                                expected: Some(format!("length in [{}, {}]", min, max)),
601                                actual: Some(len.to_string()),
602                                constraint: Some("length".to_string()),
603                                severity: ErrorSeverity::Error,
604                                context: HashMap::new(),
605                            });
606                        }
607                    }
608                }
609                Constraint::NotNull => {
610                    if value.is_null() {
611                        errors.push(ValidationError {
612                            errortype: ValidationErrorType::ConstraintViolation,
613                            fieldpath: fieldpath.to_string(),
614                            message: "Value cannot be null".to_string(),
615                            expected: Some("non-null value".to_string()),
616                            actual: Some("null".to_string()),
617                            constraint: Some("not_null".to_string()),
618                            severity: ErrorSeverity::Error,
619                            context: HashMap::new(),
620                        });
621                    }
622                }
623                Constraint::Unique => {
624                    if let Some(arr) = value.as_array() {
625                        let mut seen = HashSet::new();
626                        for item in arr {
627                            let item_str = item.to_string();
628                            if !seen.insert(item_str.clone()) {
629                                errors.push(ValidationError {
630                                    errortype: ValidationErrorType::DuplicateValues,
631                                    fieldpath: fieldpath.to_string(),
632                                    message: item_str.to_string(),
633                                    expected: Some("unique values".to_string()),
634                                    actual: Some("duplicate found".to_string()),
635                                    constraint: Some("unique".to_string()),
636                                    severity: ErrorSeverity::Error,
637                                    context: HashMap::new(),
638                                });
639                            }
640                        }
641                    }
642                }
643                Constraint::Pattern(pattern) => {
644                    if let Some(s) = value.as_str() {
645                        #[cfg(feature = "regex")]
646                        {
647                            if let Ok(re) = regex::Regex::new(pattern) {
648                                if !re.is_match(s) {
649                                    errors.push(ValidationError {
650                                        errortype: ValidationErrorType::InvalidFormat,
651                                        fieldpath: fieldpath.to_string(),
652                                        message: format!(
653                                            "Value '{}' does not match pattern '{}'",
654                                            s, pattern
655                                        ),
656                                        expected: Some(pattern.to_string()),
657                                        actual: Some(s.to_string()),
658                                        constraint: Some(pattern.to_string()),
659                                        severity: ErrorSeverity::Error,
660                                        context: HashMap::new(),
661                                    });
662                                }
663                            }
664                        }
665                        #[cfg(not(feature = "regex"))]
666                        {
667                            warnings.push(ValidationError {
668                                errortype: ValidationErrorType::SchemaError,
669                                fieldpath: fieldpath.to_string(),
670                                message: "Pattern validation requires 'regex' feature".to_string(),
671                                expected: None,
672                                actual: None,
673                                constraint: Some(pattern.to_string()),
674                                severity: ErrorSeverity::Warning,
675                                context: HashMap::new(),
676                            });
677                        }
678                    }
679                }
680                Constraint::AllowedValues(allowed) => {
681                    let value_str = match value {
682                        JsonValue::String(s) => s.clone(),
683                        _ => value.to_string(),
684                    };
685                    if !allowed.contains(&value_str) {
686                        errors.push(ValidationError {
687                            errortype: ValidationErrorType::ConstraintViolation,
688                            fieldpath: fieldpath.to_string(),
689                            message: format!(
690                                "Value '{}' is not in allowed values: {:?}",
691                                value_str, allowed
692                            ),
693                            expected: Some(format!("{allowed:?}")),
694                            actual: Some(value_str),
695                            constraint: Some("allowed_values".to_string()),
696                            severity: ErrorSeverity::Error,
697                            context: HashMap::new(),
698                        });
699                    }
700                }
701                Constraint::Precision { decimal_places } => {
702                    if let Some(num) = value.as_f64() {
703                        let num_str = num.to_string();
704                        if let Some(dot_pos) = num_str.find('.') {
705                            let actual_precision = num_str.len() - dot_pos - 1;
706                            if actual_precision > *decimal_places {
707                                errors.push(ValidationError {
708                                    errortype: ValidationErrorType::ConstraintViolation,
709                                    fieldpath: fieldpath.to_string(),
710                                    message: format!(
711                                        "Value {} has {} decimal places, expected at most {}",
712                                        num, actual_precision, decimal_places
713                                    ),
714                                    expected: Some(format!(
715                                        "max {} decimal places",
716                                        decimal_places
717                                    )),
718                                    actual: Some(format!("{} decimal places", actual_precision)),
719                                    constraint: Some("precision".to_string()),
720                                    severity: ErrorSeverity::Error,
721                                    context: HashMap::new(),
722                                });
723                            }
724                        }
725                    }
726                }
727                Constraint::ArraySize { min, max } => {
728                    if let Some(arr) = value.as_array() {
729                        let size = arr.len();
730                        if size < *min || size > *max {
731                            errors.push(ValidationError {
732                                errortype: ValidationErrorType::ConstraintViolation,
733                                fieldpath: fieldpath.to_string(),
734                                message: format!(
735                                    "Array size {} is out of range [{}, {}]",
736                                    size, min, max
737                                ),
738                                expected: Some(format!("size in [{}, {}]", min, max)),
739                                actual: Some(size.to_string()),
740                                constraint: Some("array_size".to_string()),
741                                severity: ErrorSeverity::Error,
742                                context: HashMap::new(),
743                            });
744                        }
745                    }
746                }
747                Constraint::ArrayElements(element_constraint) => {
748                    if let Some(arr) = value.as_array() {
749                        for (idx, element) in arr.iter().enumerate() {
750                            let element_path = format!("{}[{}]", fieldpath, idx);
751                            self.validate_field_constraints(
752                                element,
753                                &[(**element_constraint).clone()],
754                                &element_path,
755                                errors,
756                                warnings,
757                                stats,
758                            )?;
759                        }
760                    }
761                }
762                Constraint::Custom(_rule_name) => {
763                    // Custom constraint validation is handled separately in validate_fields
764                    // This is just a placeholder for consistency
765                }
766                Constraint::Statistical(stats_constraints) => {
767                    // Validate statistical properties of numeric arrays
768                    if let Some(arr) = value.as_array() {
769                        let mut numeric_values: Vec<f64> = Vec::new();
770
771                        // Extract numeric values from array
772                        for (idx, val) in arr.iter().enumerate() {
773                            if let Some(num) = val.as_f64() {
774                                numeric_values.push(num);
775                            } else if let Some(num) = val.as_i64() {
776                                numeric_values.push(num as f64);
777                            } else {
778                                errors.push(ValidationError {
779                                    errortype: ValidationErrorType::TypeMismatch,
780                                    fieldpath: format!("{}[{}]", fieldpath, idx),
781                                    message: format!("{val}"),
782                                    expected: Some("number".to_string()),
783                                    actual: Some(val.to_string()),
784                                    constraint: Some("statistical".to_string()),
785                                    severity: ErrorSeverity::Error,
786                                    context: HashMap::new(),
787                                });
788                                continue;
789                            }
790                        }
791
792                        if numeric_values.is_empty() {
793                            errors.push(ValidationError {
794                                errortype: ValidationErrorType::ConstraintViolation,
795                                fieldpath: fieldpath.to_string(),
796                                message: "Statistical validation requires numeric values"
797                                    .to_string(),
798                                expected: Some("numeric array".to_string()),
799                                actual: Some("empty or non-numeric array".to_string()),
800                                constraint: Some("statistical".to_string()),
801                                severity: ErrorSeverity::Error,
802                                context: HashMap::new(),
803                            });
804                        } else {
805                            // Calculate statistics
806                            let count = numeric_values.len() as f64;
807                            let mean = numeric_values.iter().sum::<f64>() / count;
808
809                            // Calculate standard deviation
810                            let variance = numeric_values
811                                .iter()
812                                .map(|x| (x - mean).powi(2))
813                                .sum::<f64>()
814                                / count;
815                            let std_dev = variance.sqrt();
816
817                            // Check mean constraints
818                            if let Some(min_mean) = stats_constraints.min_mean {
819                                if mean < min_mean {
820                                    errors.push(ValidationError {
821                                        errortype: ValidationErrorType::ConstraintViolation,
822                                        fieldpath: fieldpath.to_string(),
823                                        message: format!(
824                                            "Mean {:.4} is less than minimum {:.4}",
825                                            mean, min_mean
826                                        ),
827                                        expected: Some(format!(":.4{min_mean}")),
828                                        actual: Some(format!(":.4{mean}")),
829                                        constraint: Some("statistical.min_mean".to_string()),
830                                        severity: ErrorSeverity::Error,
831                                        context: HashMap::new(),
832                                    });
833                                }
834                            }
835
836                            if let Some(max_mean) = stats_constraints.max_mean {
837                                if mean > max_mean {
838                                    errors.push(ValidationError {
839                                        errortype: ValidationErrorType::ConstraintViolation,
840                                        fieldpath: fieldpath.to_string(),
841                                        message: format!(
842                                            "Mean {:.4} exceeds maximum {:.4}",
843                                            mean, max_mean
844                                        ),
845                                        expected: Some(format!(":.4{max_mean}")),
846                                        actual: Some(format!(":.4{mean}")),
847                                        constraint: Some("statistical.max_mean".to_string()),
848                                        severity: ErrorSeverity::Error,
849                                        context: HashMap::new(),
850                                    });
851                                }
852                            }
853
854                            // Check standard deviation constraints
855                            if let Some(min_std) = stats_constraints.min_std {
856                                if std_dev < min_std {
857                                    errors.push(ValidationError {
858                                        errortype: ValidationErrorType::ConstraintViolation,
859                                        fieldpath: fieldpath.to_string(),
860                                        message: format!(
861                                            "Standard deviation {:.4} is less than minimum {:.4}",
862                                            std_dev, min_std
863                                        ),
864                                        expected: Some(format!(":.4{min_std}")),
865                                        actual: Some(format!(":.4{std_dev}")),
866                                        constraint: Some("statistical.min_std".to_string()),
867                                        severity: ErrorSeverity::Error,
868                                        context: HashMap::new(),
869                                    });
870                                }
871                            }
872
873                            if let Some(max_std) = stats_constraints.max_std {
874                                if std_dev > max_std {
875                                    errors.push(ValidationError {
876                                        errortype: ValidationErrorType::ConstraintViolation,
877                                        fieldpath: fieldpath.to_string(),
878                                        message: format!(
879                                            "Standard deviation {:.4} exceeds maximum {:.4}",
880                                            std_dev, max_std
881                                        ),
882                                        expected: Some(format!(":.4{max_std}")),
883                                        actual: Some(format!(":.4{std_dev}")),
884                                        constraint: Some("statistical.max_std".to_string()),
885                                        severity: ErrorSeverity::Error,
886                                        context: HashMap::new(),
887                                    });
888                                }
889                            }
890
891                            // Check distribution (if specified)
892                            if let Some(expected_dist) = &stats_constraints.expected_distribution {
893                                // For now, just add a warning - full distribution testing would require more complex analysis
894                                warnings.push(ValidationError {
895                                    errortype: ValidationErrorType::SchemaError,
896                                    fieldpath: fieldpath.to_string(),
897                                    message: format!(
898                                        "Distribution testing for '{}' not yet implemented",
899                                        expected_dist
900                                    ),
901                                    expected: None,
902                                    actual: None,
903                                    constraint: Some("statistical.distribution".to_string()),
904                                    severity: ErrorSeverity::Warning,
905                                    context: HashMap::new(),
906                                });
907                            }
908                        }
909                    } else {
910                        errors.push(ValidationError {
911                            errortype: ValidationErrorType::TypeMismatch,
912                            fieldpath: fieldpath.to_string(),
913                            message: "Statistical constraints require an array of numeric values"
914                                .to_string(),
915                            expected: Some("numeric array".to_string()),
916                            actual: Some(format!("{value}")),
917                            constraint: Some("statistical".to_string()),
918                            severity: ErrorSeverity::Error,
919                            context: HashMap::new(),
920                        });
921                    }
922                }
923                Constraint::Temporal(time_constraints) => {
924                    // Validate temporal data (array of timestamps)
925                    if let Some(arr) = value.as_array() {
926                        let mut timestamps: Vec<i64> = Vec::new();
927
928                        // Extract timestamps from array
929                        for (idx, val) in arr.iter().enumerate() {
930                            if let Some(ts) = val.as_i64() {
931                                timestamps.push(ts);
932                            } else if let Some(ts) = val.as_f64() {
933                                timestamps.push(ts as i64);
934                            } else {
935                                errors.push(ValidationError {
936                                    errortype: ValidationErrorType::TypeMismatch,
937                                    fieldpath: format!("{}[{}]", fieldpath, idx),
938                                    message: format!("{val}"),
939                                    expected: Some("timestamp (integer or float)".to_string()),
940                                    actual: Some(val.to_string()),
941                                    constraint: Some("temporal".to_string()),
942                                    severity: ErrorSeverity::Error,
943                                    context: HashMap::new(),
944                                });
945                                continue;
946                            }
947                        }
948
949                        if timestamps.len() < 2 {
950                            errors.push(ValidationError {
951                                errortype: ValidationErrorType::ConstraintViolation,
952                                fieldpath: fieldpath.to_string(),
953                                message: "Temporal validation requires at least 2 timestamps"
954                                    .to_string(),
955                                expected: Some("at least 2 timestamps".to_string()),
956                                actual: Some(format!("{} timestamps", timestamps.len())),
957                                constraint: Some("temporal".to_string()),
958                                severity: ErrorSeverity::Error,
959                                context: HashMap::new(),
960                            });
961                        } else {
962                            // Check for monotonic ordering if required
963                            if time_constraints.require_monotonic {
964                                let mut _is_monotonic = true;
965                                for i in 1..timestamps.len() {
966                                    if timestamps[i] < timestamps[i.saturating_sub(1)] {
967                                        _is_monotonic = false;
968                                        errors.push(ValidationError {
969                                            errortype: ValidationErrorType::ConstraintViolation,
970                                            fieldpath: fieldpath.to_string(),
971                                            message: format!(
972                                                "Timestamps not monotonic: {} comes after {}",
973                                                timestamps[i],
974                                                timestamps[i.saturating_sub(1)]
975                                            ),
976                                            expected: Some(
977                                                "monotonic increasing timestamps".to_string(),
978                                            ),
979                                            actual: Some("non-monotonic timestamps".to_string()),
980                                            constraint: Some("temporal.monotonic".to_string()),
981                                            severity: ErrorSeverity::Error,
982                                            context: HashMap::new(),
983                                        });
984                                        break;
985                                    }
986                                }
987                            }
988
989                            // Check for duplicates if not allowed
990                            if !time_constraints.allow_duplicates {
991                                let mut seen = std::collections::HashSet::new();
992                                for &ts in &timestamps {
993                                    if !seen.insert(ts) {
994                                        errors.push(ValidationError {
995                                            errortype: ValidationErrorType::ConstraintViolation,
996                                            fieldpath: fieldpath.to_string(),
997                                            message: format!("{ts}"),
998                                            expected: Some("unique timestamps".to_string()),
999                                            actual: Some("duplicate timestamps".to_string()),
1000                                            constraint: Some("temporal.unique".to_string()),
1001                                            severity: ErrorSeverity::Error,
1002                                            context: HashMap::new(),
1003                                        });
1004                                        break;
1005                                    }
1006                                }
1007                            }
1008
1009                            // Check interval constraints
1010                            for i in 1..timestamps.len() {
1011                                let interval_ms =
1012                                    (timestamps[i] - timestamps[i.saturating_sub(1)]).abs();
1013                                let interval = std::time::Duration::from_millis(interval_ms as u64);
1014
1015                                if let Some(min_interval) = &time_constraints.min_interval {
1016                                    if interval < *min_interval {
1017                                        errors.push(ValidationError {
1018                                            errortype: ValidationErrorType::ConstraintViolation,
1019                                            fieldpath: fieldpath.to_string(),
1020                                            message: format!(
1021                                                "Interval {:?} is less than minimum {:?}",
1022                                                interval, min_interval
1023                                            ),
1024                                            expected: Some(format!(
1025                                                "min interval {:?}",
1026                                                min_interval
1027                                            )),
1028                                            actual: Some(format!("{:?}", interval)),
1029                                            constraint: Some("temporal.min_interval".to_string()),
1030                                            severity: ErrorSeverity::Error,
1031                                            context: HashMap::new(),
1032                                        });
1033                                        break;
1034                                    }
1035                                }
1036
1037                                if let Some(max_interval) = &time_constraints.max_interval {
1038                                    if interval > *max_interval {
1039                                        errors.push(ValidationError {
1040                                            errortype: ValidationErrorType::ConstraintViolation,
1041                                            fieldpath: fieldpath.to_string(),
1042                                            message: format!(
1043                                                "Interval {:?} exceeds maximum {:?}",
1044                                                interval, max_interval
1045                                            ),
1046                                            expected: Some(format!(
1047                                                "max interval {:?}",
1048                                                max_interval
1049                                            )),
1050                                            actual: Some(format!("{:?}", interval)),
1051                                            constraint: Some("temporal.max_interval".to_string()),
1052                                            severity: ErrorSeverity::Error,
1053                                            context: HashMap::new(),
1054                                        });
1055                                        break;
1056                                    }
1057                                }
1058                            }
1059                        }
1060                    } else {
1061                        errors.push(ValidationError {
1062                            errortype: ValidationErrorType::TypeMismatch,
1063                            fieldpath: fieldpath.to_string(),
1064                            message: "Temporal constraints require an array of timestamps"
1065                                .to_string(),
1066                            expected: Some("array of timestamps".to_string()),
1067                            actual: Some(format!("{value}")),
1068                            constraint: Some("temporal".to_string()),
1069                            severity: ErrorSeverity::Error,
1070                            context: HashMap::new(),
1071                        });
1072                    }
1073                }
1074                Constraint::Shape(shape_constraints) => {
1075                    // Validate array/matrix shape properties
1076                    if let Some(arr) = value.as_array() {
1077                        // For JSON arrays, we can only validate 1D arrays directly
1078                        // Multi-dimensional arrays would need to be nested arrays
1079                        let mut shape = vec![arr.len()];
1080
1081                        // Check if it's a nested array (2D)
1082                        let mut is_2d = true;
1083                        let mut inner_sizes = Vec::new();
1084                        for elem in arr {
1085                            if let Some(inner_arr) = elem.as_array() {
1086                                inner_sizes.push(inner_arr.len());
1087                            } else {
1088                                is_2d = false;
1089                                break;
1090                            }
1091                        }
1092
1093                        if is_2d && !inner_sizes.is_empty() {
1094                            // Check if all inner arrays have the same size
1095                            let first_size = inner_sizes[0];
1096                            if inner_sizes.iter().all(|&s| s == first_size) {
1097                                shape = vec![arr.len(), first_size];
1098                            } else {
1099                                errors.push(ValidationError {
1100                                    errortype: ValidationErrorType::ShapeError,
1101                                    fieldpath: fieldpath.to_string(),
1102                                    message: "Jagged arrays are not supported - all rows must have the same length".to_string(),
1103                                    expected: Some("rectangular array".to_string()),
1104                                    actual: Some("jagged array".to_string()),
1105                                    constraint: Some(format!("{:?}", shape)),
1106                                    severity: ErrorSeverity::Error,
1107                                    context: HashMap::new(),
1108                                });
1109                                return Ok(());
1110                            }
1111                        }
1112
1113                        // Validate dimensions
1114                        if !shape_constraints.dimensions.is_empty() {
1115                            let expected_dims = &shape_constraints.dimensions;
1116                            if shape.len() != expected_dims.len() {
1117                                errors.push(ValidationError {
1118                                    errortype: ValidationErrorType::ShapeError,
1119                                    fieldpath: fieldpath.to_string(),
1120                                    message: format!(
1121                                        "Array has {} dimensions, expected {}",
1122                                        shape.len(),
1123                                        expected_dims.len()
1124                                    ),
1125                                    expected: Some(format!("{} dimensions", expected_dims.len())),
1126                                    actual: Some(format!("{} dimensions", shape.len())),
1127                                    constraint: Some("shape.dimensions".to_string()),
1128                                    severity: ErrorSeverity::Error,
1129                                    context: HashMap::new(),
1130                                });
1131                            } else {
1132                                // Check each dimension
1133                                for (idx, (actual_dim, expected_dim)) in
1134                                    shape.iter().zip(expected_dims.iter()).enumerate()
1135                                {
1136                                    if let Some(expected) = expected_dim {
1137                                        if actual_dim != expected {
1138                                            errors.push(ValidationError {
1139                                                errortype: ValidationErrorType::ShapeError,
1140                                                fieldpath: fieldpath.to_string(),
1141                                                message: format!(
1142                                                    "Dimension {} has size {}, expected {}",
1143                                                    idx, actual_dim, expected
1144                                                ),
1145                                                expected: Some(format!(
1146                                                    "dimension {} = {}",
1147                                                    idx, expected
1148                                                )),
1149                                                actual: Some(format!(
1150                                                    "dimension {} = {}",
1151                                                    idx, actual_dim
1152                                                )),
1153                                                constraint: Some(format!(
1154                                                    "shape.dimension[{}]",
1155                                                    idx
1156                                                )),
1157                                                severity: ErrorSeverity::Error,
1158                                                context: HashMap::new(),
1159                                            });
1160                                        }
1161                                    }
1162                                }
1163                            }
1164                        }
1165
1166                        // Check total element count
1167                        let total_elements: usize = shape.iter().product();
1168
1169                        if let Some(min_elements) = shape_constraints.min_elements {
1170                            if total_elements < min_elements {
1171                                errors.push(ValidationError {
1172                                    errortype: ValidationErrorType::ShapeError,
1173                                    fieldpath: fieldpath.to_string(),
1174                                    message: format!(
1175                                        "Array has {} elements, minimum required is {}",
1176                                        total_elements, min_elements
1177                                    ),
1178                                    expected: Some(format!(">= {} elements", min_elements)),
1179                                    actual: Some(format!("{} elements", total_elements)),
1180                                    constraint: Some("shape.min_elements".to_string()),
1181                                    severity: ErrorSeverity::Error,
1182                                    context: HashMap::new(),
1183                                });
1184                            }
1185                        }
1186
1187                        if let Some(max_elements) = shape_constraints.max_elements {
1188                            if total_elements > max_elements {
1189                                errors.push(ValidationError {
1190                                    errortype: ValidationErrorType::ShapeError,
1191                                    fieldpath: fieldpath.to_string(),
1192                                    message: format!(
1193                                        "Array has {} elements, maximum allowed is {}",
1194                                        total_elements, max_elements
1195                                    ),
1196                                    expected: Some(format!("<= {} elements", max_elements)),
1197                                    actual: Some(format!("{} elements", total_elements)),
1198                                    constraint: Some("shape.max_elements".to_string()),
1199                                    severity: ErrorSeverity::Error,
1200                                    context: HashMap::new(),
1201                                });
1202                            }
1203                        }
1204
1205                        // Check if square matrix is required (only for 2D arrays)
1206                        if shape_constraints.require_square
1207                            && shape.len() == 2
1208                            && shape[0] != shape[1]
1209                        {
1210                            errors.push(ValidationError {
1211                                errortype: ValidationErrorType::ShapeError,
1212                                fieldpath: fieldpath.to_string(),
1213                                message: format!(
1214                                    "Matrix must be square, but has shape {}x{}",
1215                                    shape[0], shape[1]
1216                                ),
1217                                expected: Some("square matrix".to_string()),
1218                                actual: Some(format!("{}x{} matrix", shape[0], shape[1])),
1219                                constraint: Some("shape.square".to_string()),
1220                                severity: ErrorSeverity::Error,
1221                                context: HashMap::new(),
1222                            });
1223                        }
1224                    } else {
1225                        errors.push(ValidationError {
1226                            errortype: ValidationErrorType::TypeMismatch,
1227                            fieldpath: fieldpath.to_string(),
1228                            message: "Shape constraints require an array".to_string(),
1229                            expected: Some("array".to_string()),
1230                            actual: Some(format!("{value}")),
1231                            constraint: Some("shape".to_string()),
1232                            severity: ErrorSeverity::Error,
1233                            context: HashMap::new(),
1234                        });
1235                    }
1236                }
1237                Constraint::And(constraints) => {
1238                    // All constraints must pass
1239                    for constraint in constraints {
1240                        self.validate_field_constraints(
1241                            value,
1242                            std::slice::from_ref(constraint),
1243                            fieldpath,
1244                            errors,
1245                            warnings,
1246                            stats,
1247                        )?;
1248                    }
1249                }
1250                Constraint::Or(constraints) => {
1251                    // At least one constraint must pass
1252                    let mut temperrors = Vec::new();
1253                    let mut any_passed = false;
1254
1255                    for constraint in constraints {
1256                        let mut constrainterrors = Vec::new();
1257                        let mut constraintwarnings = Vec::new();
1258                        self.validate_field_constraints(
1259                            value,
1260                            std::slice::from_ref(constraint),
1261                            fieldpath,
1262                            &mut constrainterrors,
1263                            &mut constraintwarnings,
1264                            stats,
1265                        )?;
1266
1267                        if constrainterrors.is_empty() {
1268                            any_passed = true;
1269                            break;
1270                        } else {
1271                            temperrors.extend(constrainterrors);
1272                        }
1273                    }
1274
1275                    if !any_passed {
1276                        errors.push(ValidationError {
1277                            errortype: ValidationErrorType::ConstraintViolation,
1278                            fieldpath: fieldpath.to_string(),
1279                            message: format!(
1280                                "None of the OR constraints passed: {} errors",
1281                                temperrors.len()
1282                            ),
1283                            expected: Some("at least one constraint to pass".to_string()),
1284                            actual: Some("all constraints failed".to_string()),
1285                            constraint: Some("or".to_string()),
1286                            severity: ErrorSeverity::Error,
1287                            context: HashMap::new(),
1288                        });
1289                    }
1290                }
1291                Constraint::Not(constraint) => {
1292                    // Constraint must not pass
1293                    let mut temperrors = Vec::new();
1294                    let mut temp_warnings = Vec::new();
1295                    self.validate_field_constraints(
1296                        value,
1297                        &[*constraint.clone()],
1298                        fieldpath,
1299                        &mut temperrors,
1300                        &mut temp_warnings,
1301                        stats,
1302                    )?;
1303
1304                    if temperrors.is_empty() {
1305                        errors.push(ValidationError {
1306                            errortype: ValidationErrorType::ConstraintViolation,
1307                            fieldpath: fieldpath.to_string(),
1308                            message: "NOT constraint failed: inner constraint passed".to_string(),
1309                            expected: Some("constraint to fail".to_string()),
1310                            actual: Some("constraint passed".to_string()),
1311                            constraint: Some("not".to_string()),
1312                            severity: ErrorSeverity::Error,
1313                            context: HashMap::new(),
1314                        });
1315                    }
1316                }
1317                Constraint::If {
1318                    condition,
1319                    then_constraint,
1320                    else_constraint,
1321                } => {
1322                    // Conditional constraint
1323                    let mut conditionerrors = Vec::new();
1324                    let mut condition_warnings = Vec::new();
1325                    self.validate_field_constraints(
1326                        value,
1327                        &[*condition.clone()],
1328                        fieldpath,
1329                        &mut conditionerrors,
1330                        &mut condition_warnings,
1331                        stats,
1332                    )?;
1333
1334                    if conditionerrors.is_empty() {
1335                        // Condition passed, apply then_constraint
1336                        self.validate_field_constraints(
1337                            value,
1338                            &[*then_constraint.clone()],
1339                            fieldpath,
1340                            errors,
1341                            warnings,
1342                            stats,
1343                        )?;
1344                    } else if let Some(else_constraint) = else_constraint {
1345                        // Condition failed, apply else_constraint
1346                        self.validate_field_constraints(
1347                            value,
1348                            &[*else_constraint.clone()],
1349                            fieldpath,
1350                            errors,
1351                            warnings,
1352                            stats,
1353                        )?;
1354                    }
1355                }
1356            }
1357        }
1358        Ok(())
1359    }
1360
1361    /// Validate global constraints
1362    #[allow(clippy::ptr_arg)]
1363    fn validate_global_constraints(
1364        &self,
1365        data: &JsonValue,
1366        schema: &ValidationSchema,
1367        errors: &mut Vec<ValidationError>,
1368        warnings: &mut Vec<ValidationError>,
1369        stats: &mut ValidationStats,
1370    ) -> Result<(), CoreError> {
1371        // Global constraints would be implemented here
1372        Ok(())
1373    }
1374
1375    /// Check for additional fields
1376    #[allow(clippy::ptr_arg)]
1377    fn check_additional_fields(
1378        &self,
1379        data: &JsonValue,
1380        schema: &ValidationSchema,
1381        errors: &mut Vec<ValidationError>,
1382        warnings: &mut Vec<ValidationError>,
1383    ) -> Result<(), CoreError> {
1384        if let JsonValue::Object(obj) = data {
1385            for key in obj.keys() {
1386                if !schema.fields.contains_key(key) {
1387                    errors.push(ValidationError {
1388                        errortype: ValidationErrorType::SchemaError,
1389                        fieldpath: key.clone(),
1390                        message: format!("Additional field '{}' not allowed", key),
1391                        expected: None,
1392                        actual: Some(key.clone()),
1393                        constraint: None,
1394                        severity: ErrorSeverity::Warning,
1395                        context: HashMap::new(),
1396                    });
1397                }
1398            }
1399        }
1400        Ok(())
1401    }
1402
1403    /// Get the type name for a JSON value
1404    fn get_value_type_name(&self, value: &JsonValue) -> String {
1405        match value {
1406            JsonValue::Null => "null".to_string(),
1407            JsonValue::Bool(_) => "boolean".to_string(),
1408            JsonValue::Number(n) => {
1409                if n.is_i64() {
1410                    "integer".to_string()
1411                } else {
1412                    "number".to_string()
1413                }
1414            }
1415            JsonValue::String(_) => "string".to_string(),
1416            JsonValue::Array(_) => "array".to_string(),
1417            JsonValue::Object(_) => "object".to_string(),
1418        }
1419    }
1420
1421    /// Generate cache key for validation result
1422    fn generate_cachekey(
1423        &self,
1424        data: &JsonValue,
1425        schema: &ValidationSchema,
1426    ) -> Result<String, CoreError> {
1427        let mut hasher = DefaultHasher::new();
1428        data.to_string().hash(&mut hasher);
1429        schema.name.hash(&mut hasher);
1430        schema.version.hash(&mut hasher);
1431
1432        Ok(format!("{:x}", hasher.finish()))
1433    }
1434
1435    /// Get cached validation result
1436    fn get_cached_result(&self, cachekey: &str) -> Result<Option<ValidationResult>, CoreError> {
1437        let cache = self.cache.read().map_err(|_| {
1438            CoreError::ComputationError(ErrorContext::new(
1439                "Failed to acquire cache read lock".to_string(),
1440            ))
1441        })?;
1442
1443        if let Some(entry) = cache.get(cachekey) {
1444            // Check if cache entry is still valid (for now, always valid)
1445            return Ok(Some(entry.result.clone()));
1446        }
1447
1448        Ok(None)
1449    }
1450
1451    /// Cache validation result
1452    fn cache_result(&self, cachekey: &str, result: ValidationResult) -> Result<(), CoreError> {
1453        let mut cache = self.cache.write().map_err(|_| {
1454            CoreError::ComputationError(ErrorContext::new(
1455                "Failed to acquire cache write lock".to_string(),
1456            ))
1457        })?;
1458
1459        // Remove oldest entries if cache is full
1460        if cache.len() >= self.config.cache_size_limit {
1461            if let Some((oldest_key, _)) = cache
1462                .iter()
1463                .min_by_key(|(_, entry)| entry.timestamp)
1464                .map(|(k, v)| (k.clone(), v.clone()))
1465            {
1466                cache.remove(&oldest_key);
1467            }
1468        }
1469
1470        let entry = CacheEntry {
1471            result,
1472            timestamp: Instant::now(),
1473            hit_count: 0,
1474        };
1475
1476        cache.insert(cachekey.to_string(), entry);
1477        Ok(())
1478    }
1479
1480    /// Calculate cache hit rate
1481    fn calculate_cache_hit_rate(&self) -> Result<f64, CoreError> {
1482        let cache = self.cache.read().map_err(|_| {
1483            CoreError::ComputationError(ErrorContext::new(
1484                "Failed to acquire cache read lock".to_string(),
1485            ))
1486        })?;
1487
1488        if cache.is_empty() {
1489            return Ok(0.0);
1490        }
1491
1492        let total_hits: usize = cache.values().map(|entry| entry.hit_count).sum();
1493        let total_entries = cache.len();
1494
1495        Ok(total_hits as f64 / total_entries as f64)
1496    }
1497}
1498
1499impl Default for Validator {
1500    fn default() -> Self {
1501        Self::new(ValidationConfig::default()).expect("Operation failed")
1502    }
1503}
1504
1505#[cfg(test)]
1506#[path = "validator_tests.rs"]
1507mod tests;