sklears_compose/resource_management/
constraints.rs

1//! Resource constraint validation and enforcement
2//!
3//! This module provides constraint checking capabilities for resource allocations,
4//! ensuring that security, performance, and policy requirements are met.
5
6use super::monitoring::AlertSeverity;
7use super::resource_types::{
8    AllocationPriority, ResourceAllocation, ResourceUsage, SecurityConstraints,
9};
10use crate::execution_config::ResourceConstraints;
11use crate::task_definitions::TaskRequirements;
12use sklears_core::error::{Result as SklResult, SklearsError};
13use std::collections::HashMap;
14use std::time::{Duration, SystemTime};
15
16/// Resource constraint checker for validating allocation requests
17#[derive(Debug)]
18pub struct ResourceConstraintChecker {
19    /// Global resource constraints
20    global_constraints: ResourceConstraints,
21    /// Policy rules
22    policy_rules: Vec<PolicyRule>,
23    /// Constraint validation cache
24    validation_cache: HashMap<String, ValidationResult>,
25    /// Checker configuration
26    config: ConstraintCheckerConfig,
27    /// Validation statistics
28    stats: ValidationStats,
29}
30
31/// Configuration for constraint checker
32#[derive(Debug, Clone)]
33pub struct ConstraintCheckerConfig {
34    /// Enable constraint caching
35    pub enable_caching: bool,
36    /// Cache TTL
37    pub cache_ttl: Duration,
38    /// Strict validation mode
39    pub strict_mode: bool,
40    /// Enable security validation
41    pub enable_security_validation: bool,
42    /// Enable performance validation
43    pub enable_performance_validation: bool,
44    /// Validation timeout
45    pub validation_timeout: Duration,
46}
47
48/// Policy rule for resource allocation
49#[derive(Debug, Clone)]
50pub struct PolicyRule {
51    /// Rule ID
52    pub id: String,
53    /// Rule name
54    pub name: String,
55    /// Rule description
56    pub description: String,
57    /// Rule condition
58    pub condition: PolicyCondition,
59    /// Rule action
60    pub action: PolicyAction,
61    /// Rule priority
62    pub priority: u32,
63    /// Rule enabled
64    pub enabled: bool,
65    /// Rule metadata
66    pub metadata: HashMap<String, String>,
67}
68
69/// Policy condition that must be met
70#[derive(Debug, Clone)]
71pub enum PolicyCondition {
72    /// Resource usage threshold
73    ResourceThreshold {
74        resource_type: String,
75        threshold: f64,
76        operator: ComparisonOperator,
77    },
78    /// User or group constraint
79    UserConstraint {
80        users: Vec<String>,
81        groups: Vec<String>,
82        operation: SetOperation,
83    },
84    /// Time-based constraint
85    TimeConstraint {
86        start_time: u32,       // seconds since midnight
87        end_time: u32,         // seconds since midnight
88        days_of_week: Vec<u8>, // 0-6, Sunday = 0
89    },
90    /// Security constraint
91    SecurityConstraint {
92        security_level: SecurityLevel,
93        isolation_required: bool,
94        encryption_required: bool,
95    },
96    /// Performance constraint
97    PerformanceConstraint {
98        min_performance: f64,
99        max_latency: Duration,
100        min_throughput: f64,
101    },
102    /// Custom constraint
103    Custom {
104        expression: String,
105        parameters: HashMap<String, String>,
106    },
107    /// Logical combination of conditions
108    Composite {
109        operator: LogicalOperator,
110        conditions: Vec<PolicyCondition>,
111    },
112}
113
114/// Comparison operators for constraints
115#[derive(Debug, Clone, PartialEq)]
116pub enum ComparisonOperator {
117    /// Equal
118    Equal,
119    /// NotEqual
120    NotEqual,
121    /// GreaterThan
122    GreaterThan,
123    /// GreaterThanOrEqual
124    GreaterThanOrEqual,
125    /// LessThan
126    LessThan,
127    /// LessThanOrEqual
128    LessThanOrEqual,
129}
130
131/// Set operations for user constraints
132#[derive(Debug, Clone, PartialEq)]
133pub enum SetOperation {
134    /// Include
135    Include,
136    /// Exclude
137    Exclude,
138    /// Any
139    Any,
140    /// All
141    All,
142}
143
144/// Security levels
145#[derive(Debug, Clone, PartialEq, PartialOrd)]
146pub enum SecurityLevel {
147    /// Public
148    Public,
149    /// Internal
150    Internal,
151    /// Confidential
152    Confidential,
153    /// Secret
154    Secret,
155    /// TopSecret
156    TopSecret,
157}
158
159/// Logical operators for combining conditions
160#[derive(Debug, Clone, PartialEq)]
161pub enum LogicalOperator {
162    /// And
163    And,
164    /// Or
165    Or,
166    /// Not
167    Not,
168}
169
170/// Actions to take when policy conditions are met
171#[derive(Debug, Clone)]
172pub enum PolicyAction {
173    /// Allow the allocation
174    Allow,
175    /// Deny the allocation
176    Deny { reason: String },
177    /// Modify the allocation
178    Modify {
179        modifications: AllocationModifications,
180    },
181    /// Require approval
182    RequireApproval {
183        approvers: Vec<String>,
184        timeout: Duration,
185    },
186    /// Log the allocation
187    Log { level: LogLevel, message: String },
188    /// Alert on the allocation
189    Alert {
190        severity: AlertSeverity,
191        message: String,
192        channels: Vec<String>,
193    },
194    /// Rate limit the allocation
195    RateLimit {
196        max_allocations: u32,
197        time_window: Duration,
198    },
199}
200
201/// Modifications to apply to allocations
202#[derive(Debug, Clone)]
203pub struct AllocationModifications {
204    /// Reduce CPU allocation
205    pub reduce_cpu: Option<f64>,
206    /// Reduce memory allocation
207    pub reduce_memory: Option<f64>,
208    /// Change priority
209    pub new_priority: Option<AllocationPriority>,
210    /// Add security constraints
211    pub add_security_constraints: Option<SecurityConstraints>,
212    /// Modify time limits
213    pub time_limit: Option<Duration>,
214}
215
216/// Log levels for policy actions
217#[derive(Debug, Clone, PartialEq)]
218pub enum LogLevel {
219    /// Debug
220    Debug,
221    /// Info
222    Info,
223    /// Warning
224    Warning,
225    /// Error
226    Error,
227    /// Critical
228    Critical,
229}
230
231/// Result of constraint validation
232#[derive(Debug, Clone)]
233pub struct ValidationResult {
234    /// Is allocation valid
235    pub valid: bool,
236    /// Validation errors
237    pub errors: Vec<ValidationError>,
238    /// Validation warnings
239    pub warnings: Vec<ValidationWarning>,
240    /// Applied policy actions
241    pub applied_actions: Vec<PolicyAction>,
242    /// Validation timestamp
243    pub timestamp: SystemTime,
244    /// Validation duration
245    pub duration: Duration,
246    /// Modified allocation (if any)
247    pub modified_allocation: Option<ResourceAllocation>,
248}
249
250/// Validation error
251#[derive(Debug, Clone)]
252pub struct ValidationError {
253    /// Error code
254    pub code: String,
255    /// Error message
256    pub message: String,
257    /// Related policy rule
258    pub policy_rule_id: Option<String>,
259    /// Error severity
260    pub severity: ErrorSeverity,
261    /// Additional context
262    pub context: HashMap<String, String>,
263}
264
265/// Error severity levels
266#[derive(Debug, Clone, PartialEq, PartialOrd)]
267pub enum ErrorSeverity {
268    /// Low
269    Low,
270    /// Medium
271    Medium,
272    /// High
273    High,
274    /// Critical
275    Critical,
276}
277
278/// Validation warning
279#[derive(Debug, Clone)]
280pub struct ValidationWarning {
281    /// Warning code
282    pub code: String,
283    /// Warning message
284    pub message: String,
285    /// Related policy rule
286    pub policy_rule_id: Option<String>,
287    /// Additional context
288    pub context: HashMap<String, String>,
289}
290
291/// Validation statistics
292#[derive(Debug, Clone)]
293pub struct ValidationStats {
294    /// Total validations performed
295    pub total_validations: u64,
296    /// Successful validations
297    pub successful_validations: u64,
298    /// Failed validations
299    pub failed_validations: u64,
300    /// Average validation time
301    pub avg_validation_time: Duration,
302    /// Policy rule hit counts
303    pub policy_rule_hits: HashMap<String, u64>,
304    /// Cache hit rate
305    pub cache_hit_rate: f64,
306}
307
308/// Context for constraint validation
309#[derive(Debug, Clone)]
310pub struct ValidationContext {
311    /// User requesting allocation
312    pub user: Option<String>,
313    /// User groups
314    pub groups: Vec<String>,
315    /// Current time
316    pub current_time: SystemTime,
317    /// Current resource usage
318    pub current_usage: ResourceUsage,
319    /// Available resources
320    pub available_resources: ResourceUsage,
321    /// System load
322    pub system_load: f64,
323    /// Security context
324    pub security_context: SecurityContext,
325}
326
327/// Security context for validation
328#[derive(Debug, Clone)]
329pub struct SecurityContext {
330    /// Security level
331    pub level: SecurityLevel,
332    /// Security clearance
333    pub clearance: Option<String>,
334    /// Access tokens
335    pub tokens: Vec<String>,
336    /// Trusted execution environment
337    pub tee_available: bool,
338    /// Encryption capabilities
339    pub encryption_available: bool,
340}
341
342impl Default for ResourceConstraintChecker {
343    fn default() -> Self {
344        Self::new()
345    }
346}
347
348impl ResourceConstraintChecker {
349    /// Create a new constraint checker
350    #[must_use]
351    pub fn new() -> Self {
352        Self {
353            global_constraints: ResourceConstraints::default(),
354            policy_rules: Vec::new(),
355            validation_cache: HashMap::new(),
356            config: ConstraintCheckerConfig::default(),
357            stats: ValidationStats::default(),
358        }
359    }
360
361    /// Set global resource constraints
362    pub fn set_global_constraints(&mut self, constraints: ResourceConstraints) {
363        self.global_constraints = constraints;
364    }
365
366    /// Add a policy rule
367    pub fn add_policy_rule(&mut self, rule: PolicyRule) {
368        self.policy_rules.push(rule);
369        // Sort by priority (higher priority first)
370        self.policy_rules
371            .sort_by(|a, b| b.priority.cmp(&a.priority));
372    }
373
374    /// Remove a policy rule
375    pub fn remove_policy_rule(&mut self, rule_id: &str) -> bool {
376        if let Some(pos) = self.policy_rules.iter().position(|r| r.id == rule_id) {
377            self.policy_rules.remove(pos);
378            true
379        } else {
380            false
381        }
382    }
383
384    /// Validate a resource allocation request
385    pub fn validate_allocation(
386        &mut self,
387        requirements: &TaskRequirements,
388        context: &ValidationContext,
389    ) -> SklResult<ValidationResult> {
390        let start_time = SystemTime::now();
391
392        // Check cache first
393        if self.config.enable_caching {
394            let cache_key = self.generate_cache_key(requirements, context);
395            if let Some(cached_result) = self.validation_cache.get(&cache_key) {
396                if start_time
397                    .duration_since(cached_result.timestamp)
398                    .unwrap_or(Duration::MAX)
399                    < self.config.cache_ttl
400                {
401                    return Ok(cached_result.clone());
402                }
403            }
404        }
405
406        let mut result = ValidationResult {
407            valid: true,
408            errors: Vec::new(),
409            warnings: Vec::new(),
410            applied_actions: Vec::new(),
411            timestamp: start_time,
412            duration: Duration::from_secs(0),
413            modified_allocation: None,
414        };
415
416        // Validate global constraints
417        self.validate_global_constraints(requirements, context, &mut result)?;
418
419        // Apply policy rules
420        self.apply_policy_rules(requirements, context, &mut result)?;
421
422        // Security validation
423        if self.config.enable_security_validation {
424            self.validate_security_constraints(requirements, context, &mut result)?;
425        }
426
427        // Performance validation
428        if self.config.enable_performance_validation {
429            self.validate_performance_constraints(requirements, context, &mut result)?;
430        }
431
432        // Update duration
433        result.duration = SystemTime::now()
434            .duration_since(start_time)
435            .unwrap_or(Duration::from_secs(0));
436
437        // Update statistics
438        self.update_stats(&result);
439
440        // Cache result
441        if self.config.enable_caching {
442            let cache_key = self.generate_cache_key(requirements, context);
443            self.validation_cache.insert(cache_key, result.clone());
444        }
445
446        Ok(result)
447    }
448
449    /// Validate global resource constraints
450    fn validate_global_constraints(
451        &self,
452        requirements: &TaskRequirements,
453        context: &ValidationContext,
454        result: &mut ValidationResult,
455    ) -> SklResult<()> {
456        // Check CPU constraints
457        if let Some(cpu_cores) = requirements.cpu_cores {
458            if let Some(max_cpu) = self.global_constraints.max_cpu_cores {
459                if cpu_cores > max_cpu {
460                    result.valid = false;
461                    result.errors.push(ValidationError {
462                        code: "CPU_LIMIT_EXCEEDED".to_string(),
463                        message: format!(
464                            "Requested {cpu_cores} CPU cores exceeds limit of {max_cpu}"
465                        ),
466                        policy_rule_id: None,
467                        severity: ErrorSeverity::High,
468                        context: HashMap::new(),
469                    });
470                }
471            }
472        }
473
474        // Check memory constraints
475        if let Some(memory) = requirements.memory {
476            if let Some(max_memory) = self.global_constraints.max_memory {
477                if memory > max_memory {
478                    result.valid = false;
479                    result.errors.push(ValidationError {
480                        code: "MEMORY_LIMIT_EXCEEDED".to_string(),
481                        message: format!(
482                            "Requested {memory} bytes memory exceeds limit of {max_memory}"
483                        ),
484                        policy_rule_id: None,
485                        severity: ErrorSeverity::High,
486                        context: HashMap::new(),
487                    });
488                }
489            }
490        }
491
492        // Check GPU constraints
493        if !requirements.gpu_devices.is_empty() {
494            if let Some(max_gpus) = self.global_constraints.max_gpu_devices {
495                if requirements.gpu_devices.len() > max_gpus {
496                    result.valid = false;
497                    result.errors.push(ValidationError {
498                        code: "GPU_LIMIT_EXCEEDED".to_string(),
499                        message: format!(
500                            "Requested {} GPU devices exceeds limit of {}",
501                            requirements.gpu_devices.len(),
502                            max_gpus
503                        ),
504                        policy_rule_id: None,
505                        severity: ErrorSeverity::High,
506                        context: HashMap::new(),
507                    });
508                }
509            }
510        }
511
512        Ok(())
513    }
514
515    /// Apply policy rules to the allocation request
516    fn apply_policy_rules(
517        &mut self,
518        requirements: &TaskRequirements,
519        context: &ValidationContext,
520        result: &mut ValidationResult,
521    ) -> SklResult<()> {
522        for rule in &self.policy_rules {
523            if !rule.enabled {
524                continue;
525            }
526
527            // Evaluate rule condition
528            if self.evaluate_condition(&rule.condition, requirements, context)? {
529                // Update hit count
530                *self
531                    .stats
532                    .policy_rule_hits
533                    .entry(rule.id.clone())
534                    .or_insert(0) += 1;
535
536                // Apply rule action
537                match &rule.action {
538                    PolicyAction::Allow => {
539                        // No action needed, allocation is allowed
540                    }
541                    PolicyAction::Deny { reason } => {
542                        result.valid = false;
543                        result.errors.push(ValidationError {
544                            code: "POLICY_DENIED".to_string(),
545                            message: reason.clone(),
546                            policy_rule_id: Some(rule.id.clone()),
547                            severity: ErrorSeverity::High,
548                            context: HashMap::new(),
549                        });
550                    }
551                    PolicyAction::Modify { modifications } => {
552                        // Apply modifications - this would modify the allocation
553                        result.warnings.push(ValidationWarning {
554                            code: "ALLOCATION_MODIFIED".to_string(),
555                            message: "Allocation was modified by policy rule".to_string(),
556                            policy_rule_id: Some(rule.id.clone()),
557                            context: HashMap::new(),
558                        });
559                    }
560                    PolicyAction::RequireApproval {
561                        approvers: _,
562                        timeout: _,
563                    } => {
564                        result.warnings.push(ValidationWarning {
565                            code: "APPROVAL_REQUIRED".to_string(),
566                            message: "Allocation requires approval".to_string(),
567                            policy_rule_id: Some(rule.id.clone()),
568                            context: HashMap::new(),
569                        });
570                    }
571                    PolicyAction::Log { level: _, message } => {
572                        // Log the allocation (would integrate with logging system)
573                        result.warnings.push(ValidationWarning {
574                            code: "LOGGED_ALLOCATION".to_string(),
575                            message: message.clone(),
576                            policy_rule_id: Some(rule.id.clone()),
577                            context: HashMap::new(),
578                        });
579                    }
580                    PolicyAction::Alert {
581                        severity: _,
582                        message,
583                        channels: _,
584                    } => {
585                        result.warnings.push(ValidationWarning {
586                            code: "ALERT_TRIGGERED".to_string(),
587                            message: message.clone(),
588                            policy_rule_id: Some(rule.id.clone()),
589                            context: HashMap::new(),
590                        });
591                    }
592                    PolicyAction::RateLimit {
593                        max_allocations: _,
594                        time_window: _,
595                    } => {
596                        // Would implement rate limiting logic
597                        result.warnings.push(ValidationWarning {
598                            code: "RATE_LIMITED".to_string(),
599                            message: "Allocation is subject to rate limiting".to_string(),
600                            policy_rule_id: Some(rule.id.clone()),
601                            context: HashMap::new(),
602                        });
603                    }
604                }
605
606                result.applied_actions.push(rule.action.clone());
607            }
608        }
609
610        Ok(())
611    }
612
613    /// Evaluate a policy condition
614    fn evaluate_condition(
615        &self,
616        condition: &PolicyCondition,
617        requirements: &TaskRequirements,
618        context: &ValidationContext,
619    ) -> SklResult<bool> {
620        match condition {
621            PolicyCondition::ResourceThreshold {
622                resource_type,
623                threshold,
624                operator,
625            } => {
626                let current_usage = match resource_type.as_str() {
627                    "cpu" => context.current_usage.cpu_percent,
628                    "memory" => {
629                        (context.current_usage.memory_usage.used as f64
630                            / context.current_usage.memory_usage.total as f64)
631                            * 100.0
632                    }
633                    _ => return Ok(false),
634                };
635
636                Ok(self.compare_values(current_usage, *threshold, operator))
637            }
638            PolicyCondition::UserConstraint {
639                users,
640                groups,
641                operation,
642            } => {
643                if let Some(user) = &context.user {
644                    let user_match = users.contains(user);
645                    let group_match = groups.iter().any(|g| context.groups.contains(g));
646
647                    Ok(match operation {
648                        SetOperation::Include => user_match || group_match,
649                        SetOperation::Exclude => !user_match && !group_match,
650                        SetOperation::Any => user_match || group_match,
651                        SetOperation::All => {
652                            user_match && groups.iter().all(|g| context.groups.contains(g))
653                        }
654                    })
655                } else {
656                    Ok(false)
657                }
658            }
659            PolicyCondition::TimeConstraint {
660                start_time,
661                end_time,
662                days_of_week,
663            } => {
664                // Simplified time checking - would need proper timezone handling
665                let now = context
666                    .current_time
667                    .duration_since(std::time::UNIX_EPOCH)
668                    .unwrap_or(Duration::from_secs(0));
669                let seconds_today = (now.as_secs() % (24 * 3600)) as u32;
670                let day_of_week = ((now.as_secs() / (24 * 3600)) % 7) as u8; // Approximate
671
672                let time_match = if start_time <= end_time {
673                    seconds_today >= *start_time && seconds_today <= *end_time
674                } else {
675                    seconds_today >= *start_time || seconds_today <= *end_time
676                };
677
678                let day_match = days_of_week.is_empty() || days_of_week.contains(&day_of_week);
679
680                Ok(time_match && day_match)
681            }
682            PolicyCondition::SecurityConstraint {
683                security_level,
684                isolation_required,
685                encryption_required,
686            } => {
687                let level_match = context.security_context.level >= *security_level;
688                let isolation_match = !isolation_required || context.security_context.tee_available;
689                let encryption_match =
690                    !encryption_required || context.security_context.encryption_available;
691
692                Ok(level_match && isolation_match && encryption_match)
693            }
694            PolicyCondition::PerformanceConstraint {
695                min_performance: _,
696                max_latency: _,
697                min_throughput: _,
698            } => {
699                // Would need performance metrics to evaluate
700                Ok(true) // Simplified for now
701            }
702            PolicyCondition::Custom {
703                expression: _,
704                parameters: _,
705            } => {
706                // Would need a rule engine to evaluate custom expressions
707                Ok(false) // Simplified for now
708            }
709            PolicyCondition::Composite {
710                operator,
711                conditions,
712            } => match operator {
713                LogicalOperator::And => {
714                    for cond in conditions {
715                        if !self.evaluate_condition(cond, requirements, context)? {
716                            return Ok(false);
717                        }
718                    }
719                    Ok(true)
720                }
721                LogicalOperator::Or => {
722                    for cond in conditions {
723                        if self.evaluate_condition(cond, requirements, context)? {
724                            return Ok(true);
725                        }
726                    }
727                    Ok(false)
728                }
729                LogicalOperator::Not => {
730                    if conditions.len() != 1 {
731                        return Err(SklearsError::ResourceAllocationError(
732                            "NOT operator requires exactly one condition".to_string(),
733                        ));
734                    }
735                    Ok(!self.evaluate_condition(&conditions[0], requirements, context)?)
736                }
737            },
738        }
739    }
740
741    /// Compare two values using the specified operator
742    fn compare_values(&self, left: f64, right: f64, operator: &ComparisonOperator) -> bool {
743        match operator {
744            ComparisonOperator::Equal => (left - right).abs() < f64::EPSILON,
745            ComparisonOperator::NotEqual => (left - right).abs() >= f64::EPSILON,
746            ComparisonOperator::GreaterThan => left > right,
747            ComparisonOperator::GreaterThanOrEqual => left >= right,
748            ComparisonOperator::LessThan => left < right,
749            ComparisonOperator::LessThanOrEqual => left <= right,
750        }
751    }
752
753    /// Validate security constraints
754    fn validate_security_constraints(
755        &self,
756        requirements: &TaskRequirements,
757        context: &ValidationContext,
758        result: &mut ValidationResult,
759    ) -> SklResult<()> {
760        // Check if security requirements can be met
761        // TODO: Add security_constraints field to TaskRequirements
762        // TODO: Implement security validation when security_constraints field is added to TaskRequirements
763        /*
764        if let Some(security_constraints) = &requirements.security_constraints {
765            if security_constraints.isolation_required && !context.security_context.tee_available {
766                result.errors.push(ValidationError {
767                    code: "ISOLATION_NOT_AVAILABLE".to_string(),
768                    message: "Isolation required but TEE not available".to_string(),
769                    policy_rule_id: None,
770                    severity: ErrorSeverity::High,
771                    context: HashMap::new(),
772                });
773                result.valid = false;
774            }
775        }
776        */
777
778        Ok(())
779    }
780
781    /// Validate performance constraints
782    fn validate_performance_constraints(
783        &self,
784        _requirements: &TaskRequirements,
785        context: &ValidationContext,
786        result: &mut ValidationResult,
787    ) -> SklResult<()> {
788        // Check if system can meet performance requirements
789        if context.system_load > 0.9 {
790            result.warnings.push(ValidationWarning {
791                code: "HIGH_SYSTEM_LOAD".to_string(),
792                message: "System load is high, performance may be degraded".to_string(),
793                policy_rule_id: None,
794                context: HashMap::new(),
795            });
796        }
797
798        Ok(())
799    }
800
801    /// Generate cache key for validation result
802    fn generate_cache_key(
803        &self,
804        requirements: &TaskRequirements,
805        context: &ValidationContext,
806    ) -> String {
807        use std::collections::hash_map::DefaultHasher;
808        use std::hash::{Hash, Hasher};
809
810        let mut hasher = DefaultHasher::new();
811        requirements.cpu_cores.hash(&mut hasher);
812        requirements.memory.hash(&mut hasher);
813        requirements.gpu_devices.hash(&mut hasher);
814        context.user.hash(&mut hasher);
815        context.groups.hash(&mut hasher);
816
817        format!("validation_{}", hasher.finish())
818    }
819
820    /// Update validation statistics
821    fn update_stats(&mut self, result: &ValidationResult) {
822        self.stats.total_validations += 1;
823        if result.valid {
824            self.stats.successful_validations += 1;
825        } else {
826            self.stats.failed_validations += 1;
827        }
828
829        // Update average validation time
830        let total_time = self.stats.avg_validation_time.as_nanos() as f64
831            * (self.stats.total_validations - 1) as f64
832            + result.duration.as_nanos() as f64;
833        self.stats.avg_validation_time =
834            Duration::from_nanos((total_time / self.stats.total_validations as f64) as u64);
835    }
836
837    /// Get validation statistics
838    #[must_use]
839    pub fn get_stats(&self) -> &ValidationStats {
840        &self.stats
841    }
842}
843
844impl Default for ConstraintCheckerConfig {
845    fn default() -> Self {
846        Self {
847            enable_caching: true,
848            cache_ttl: Duration::from_secs(300), // 5 minutes
849            strict_mode: false,
850            enable_security_validation: true,
851            enable_performance_validation: true,
852            validation_timeout: Duration::from_secs(10),
853        }
854    }
855}
856
857impl Default for ValidationStats {
858    fn default() -> Self {
859        Self {
860            total_validations: 0,
861            successful_validations: 0,
862            failed_validations: 0,
863            avg_validation_time: Duration::from_millis(0),
864            policy_rule_hits: HashMap::new(),
865            cache_hit_rate: 0.0,
866        }
867    }
868}