1use std::collections::HashMap;
2use std::time::{Duration, Instant};
3
4pub struct QualityValidator {
6 rules: Vec<ValidationRule>,
8 rule_stats: HashMap<String, RuleStats>,
10 config: ValidationConfig,
12}
13
14#[derive(Debug, Clone)]
16pub struct ValidationRule {
17 pub id: String,
19 pub name: String,
21 pub description: String,
23 pub category: RuleCategory,
25 pub severity: ValidationSeverity,
27 pub enabled: bool,
29 pub validator: ValidationFunction,
31}
32
33#[derive(Debug, Clone, PartialEq)]
35pub enum RuleCategory {
36 MemorySafety,
38 Performance,
40 CodeStyle,
42 ErrorHandling,
44 ThreadSafety,
46 ResourceManagement,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
52pub enum ValidationSeverity {
53 Info,
55 Style,
57 Warning,
59 Error,
61 Critical,
63}
64
65pub type ValidationFunction = fn(&ValidationContext) -> Result<(), ValidationError>;
67
68#[derive(Debug)]
70pub struct ValidationContext {
71 pub operation_name: String,
73 pub metrics: OperationMetrics,
75 pub memory_info: MemoryInfo,
77 pub error_handling: ErrorHandlingInfo,
79 pub thread_safety: ThreadSafetyInfo,
81}
82
83#[derive(Debug, Clone)]
85pub struct OperationMetrics {
86 pub avg_duration: Duration,
88 pub peak_memory: usize,
90 pub success_rate: f64,
92 pub allocation_count: usize,
94 pub cpu_usage: f64,
96}
97
98#[derive(Debug, Clone)]
100pub struct MemoryInfo {
101 pub current_usage: usize,
103 pub peak_usage: usize,
105 pub active_allocations: usize,
107 pub fragmentation_ratio: f64,
109 pub growth_rate: f64,
111}
112
113#[derive(Debug, Clone)]
115pub struct ErrorHandlingInfo {
116 pub has_error_handling: bool,
118 pub error_points: usize,
120 pub handled_error_points: usize,
122 pub has_recovery: bool,
124}
125
126#[derive(Debug, Clone)]
128pub struct ThreadSafetyInfo {
129 pub is_thread_safe: bool,
131 pub shared_resources: usize,
133 pub has_synchronization: bool,
135 pub contention_level: f64,
137}
138
139#[derive(Debug, Clone)]
141pub struct ValidationError {
142 pub message: String,
144 pub suggestion: Option<String>,
146 pub location: Option<String>,
148}
149
150#[derive(Debug, Clone)]
152pub struct ValidationResult {
153 pub status: ValidationStatus,
155 pub rule_results: Vec<RuleResult>,
157 pub summary: ValidationSummary,
159 pub validation_overhead: Duration,
161}
162
163#[derive(Debug, Clone, PartialEq)]
165pub enum ValidationStatus {
166 Passed,
168 WarningsFound,
170 ErrorsFound,
172 CriticalIssuesFound,
174}
175
176#[derive(Debug, Clone)]
178pub struct RuleResult {
179 pub rule_id: String,
181 pub passed: bool,
183 pub error: Option<ValidationError>,
185 pub execution_time: Duration,
187}
188
189#[derive(Debug, Clone)]
191pub struct ValidationSummary {
192 pub total_rules: usize,
194 pub passed_rules: usize,
196 pub failed_rules: usize,
198 pub critical_issues: usize,
200 pub errors: usize,
202 pub warnings: usize,
204 pub quality_score: f64,
206}
207
208#[derive(Debug, Clone)]
210pub struct ValidationConfig {
211 pub fail_fast: bool,
213 pub max_validation_time: Duration,
215 pub enable_deep_checks: bool,
217 pub min_severity: ValidationSeverity,
219}
220
221#[derive(Debug, Clone)]
223pub struct RuleStats {
224 execution_count: usize,
226 total_time: Duration,
228 failure_count: usize,
230 avg_time: Duration,
232}
233
234impl QualityValidator {
235 pub fn new() -> Self {
237 let mut validator = Self {
238 rules: Vec::new(),
239 rule_stats: HashMap::new(),
240 config: ValidationConfig::default(),
241 };
242
243 validator.add_default_rules();
244 validator
245 }
246
247 pub fn with_config(config: ValidationConfig) -> Self {
249 let mut validator = Self {
250 rules: Vec::new(),
251 rule_stats: HashMap::new(),
252 config,
253 };
254
255 validator.add_default_rules();
256 validator
257 }
258
259 pub fn add_rule(&mut self, rule: ValidationRule) {
261 self.rules.push(rule);
262 }
263
264 pub fn remove_rule(&mut self, rule_id: &str) -> bool {
266 if let Some(pos) = self.rules.iter().position(|r| r.id == rule_id) {
267 self.rules.remove(pos);
268 self.rule_stats.remove(rule_id);
269 true
270 } else {
271 false
272 }
273 }
274
275 pub fn set_rule_enabled(&mut self, rule_id: &str, enabled: bool) -> bool {
277 if let Some(rule) = self.rules.iter_mut().find(|r| r.id == rule_id) {
278 rule.enabled = enabled;
279 true
280 } else {
281 false
282 }
283 }
284
285 pub fn validate(&mut self, context: &ValidationContext) -> ValidationResult {
287 let start_time = Instant::now();
288 let mut rule_results = Vec::new();
289 let mut should_stop = false;
290
291 let rules_info: Vec<_> = self
293 .rules
294 .iter()
295 .filter(|rule| rule.enabled)
296 .map(|rule| (rule.id.clone(), rule.severity.clone(), rule.validator))
297 .collect();
298
299 for (rule_id, severity, validator) in rules_info {
300 if should_stop && self.config.fail_fast {
301 break;
302 }
303
304 let rule_start = Instant::now();
305 let result = validator(context);
306 let rule_duration = rule_start.elapsed();
307
308 self.update_rule_stats(&rule_id, rule_duration, result.is_err());
310
311 let rule_result = RuleResult {
312 rule_id,
313 passed: result.is_ok(),
314 error: result.err(),
315 execution_time: rule_duration,
316 };
317
318 if !rule_result.passed && severity >= ValidationSeverity::Critical {
319 should_stop = true;
320 }
321
322 rule_results.push(rule_result);
323
324 if start_time.elapsed() > self.config.max_validation_time {
326 break;
327 }
328 }
329
330 let validation_overhead = start_time.elapsed();
331 let summary = self.calculate_summary(&rule_results);
332 let status = self.determine_status(&summary);
333
334 ValidationResult {
335 status,
336 rule_results,
337 summary,
338 validation_overhead,
339 }
340 }
341
342 pub fn get_rule_statistics(&self) -> &HashMap<String, RuleStats> {
344 &self.rule_stats
345 }
346
347 pub fn reset_statistics(&mut self) {
349 self.rule_stats.clear();
350 }
351
352 fn add_default_rules(&mut self) {
353 self.add_rule(ValidationRule {
355 id: "memory_leak_check".to_string(),
356 name: "Memory Leak Detection".to_string(),
357 description: "Check for potential memory leaks in tracking operations".to_string(),
358 category: RuleCategory::MemorySafety,
359 severity: ValidationSeverity::Critical,
360 enabled: true,
361 validator: validate_memory_leaks,
362 });
363
364 self.add_rule(ValidationRule {
365 id: "allocation_overhead_check".to_string(),
366 name: "Allocation Overhead Check".to_string(),
367 description: "Ensure allocation tracking overhead is within acceptable limits"
368 .to_string(),
369 category: RuleCategory::Performance,
370 severity: ValidationSeverity::Warning,
371 enabled: true,
372 validator: validate_allocation_overhead,
373 });
374
375 self.add_rule(ValidationRule {
377 id: "tracking_latency_check".to_string(),
378 name: "Tracking Latency Check".to_string(),
379 description: "Verify allocation tracking latency is acceptable".to_string(),
380 category: RuleCategory::Performance,
381 severity: ValidationSeverity::Error,
382 enabled: true,
383 validator: validate_tracking_latency,
384 });
385
386 self.add_rule(ValidationRule {
387 id: "symbol_resolution_performance".to_string(),
388 name: "Symbol Resolution Performance".to_string(),
389 description: "Check symbol resolution performance metrics".to_string(),
390 category: RuleCategory::Performance,
391 severity: ValidationSeverity::Warning,
392 enabled: true,
393 validator: validate_symbol_performance,
394 });
395
396 self.add_rule(ValidationRule {
398 id: "error_handling_coverage".to_string(),
399 name: "Error Handling Coverage".to_string(),
400 description: "Ensure proper error handling in critical paths".to_string(),
401 category: RuleCategory::ErrorHandling,
402 severity: ValidationSeverity::Error,
403 enabled: true,
404 validator: validate_error_handling,
405 });
406
407 self.add_rule(ValidationRule {
409 id: "thread_safety_check".to_string(),
410 name: "Thread Safety Check".to_string(),
411 description: "Verify thread safety of concurrent operations".to_string(),
412 category: RuleCategory::ThreadSafety,
413 severity: ValidationSeverity::Critical,
414 enabled: true,
415 validator: validate_thread_safety,
416 });
417 }
418
419 fn update_rule_stats(&mut self, rule_id: &str, duration: Duration, failed: bool) {
420 let stats = self
421 .rule_stats
422 .entry(rule_id.to_string())
423 .or_insert(RuleStats {
424 execution_count: 0,
425 total_time: Duration::ZERO,
426 failure_count: 0,
427 avg_time: Duration::ZERO,
428 });
429
430 stats.execution_count += 1;
431 stats.total_time += duration;
432 if failed {
433 stats.failure_count += 1;
434 }
435 stats.avg_time = stats.total_time / stats.execution_count as u32;
436 }
437
438 fn calculate_summary(&self, results: &[RuleResult]) -> ValidationSummary {
439 let total_rules = results.len();
440 let passed_rules = results.iter().filter(|r| r.passed).count();
441 let failed_rules = total_rules - passed_rules;
442
443 let mut critical_issues = 0;
444 let mut errors = 0;
445 let mut warnings = 0;
446
447 for result in results {
448 if !result.passed {
449 if let Some(rule) = self.rules.iter().find(|r| r.id == result.rule_id) {
450 match rule.severity {
451 ValidationSeverity::Critical => critical_issues += 1,
452 ValidationSeverity::Error => errors += 1,
453 ValidationSeverity::Warning => warnings += 1,
454 _ => {}
455 }
456 }
457 }
458 }
459
460 let quality_score = if total_rules > 0 {
461 passed_rules as f64 / total_rules as f64
462 } else {
463 1.0
464 };
465
466 ValidationSummary {
467 total_rules,
468 passed_rules,
469 failed_rules,
470 critical_issues,
471 errors,
472 warnings,
473 quality_score,
474 }
475 }
476
477 fn determine_status(&self, summary: &ValidationSummary) -> ValidationStatus {
478 if summary.critical_issues > 0 {
479 ValidationStatus::CriticalIssuesFound
480 } else if summary.errors > 0 {
481 ValidationStatus::ErrorsFound
482 } else if summary.warnings > 0 {
483 ValidationStatus::WarningsFound
484 } else {
485 ValidationStatus::Passed
486 }
487 }
488}
489
490fn validate_memory_leaks(context: &ValidationContext) -> Result<(), ValidationError> {
492 if context.memory_info.growth_rate > 10.0 * 1024.0 * 1024.0 {
493 return Err(ValidationError {
495 message: format!(
496 "High memory growth rate detected: {:.2}MB/sec",
497 context.memory_info.growth_rate / (1024.0 * 1024.0)
498 ),
499 suggestion: Some("Check for memory leaks in allocation tracking".to_string()),
500 location: Some(context.operation_name.clone()),
501 });
502 }
503
504 if context.memory_info.fragmentation_ratio > 0.5 {
505 return Err(ValidationError {
506 message: format!(
507 "High memory fragmentation: {:.1}%",
508 context.memory_info.fragmentation_ratio * 100.0
509 ),
510 suggestion: Some("Consider implementing memory compaction".to_string()),
511 location: Some(context.operation_name.clone()),
512 });
513 }
514
515 Ok(())
516}
517
518fn validate_allocation_overhead(context: &ValidationContext) -> Result<(), ValidationError> {
519 let overhead_ratio =
520 context.metrics.peak_memory as f64 / (context.memory_info.current_usage as f64).max(1.0);
521
522 if overhead_ratio > 0.1 {
523 return Err(ValidationError {
525 message: format!("High tracking overhead: {:.1}%", overhead_ratio * 100.0),
526 suggestion: Some(
527 "Optimize tracking data structures to reduce memory overhead".to_string(),
528 ),
529 location: Some(context.operation_name.clone()),
530 });
531 }
532
533 Ok(())
534}
535
536fn validate_tracking_latency(context: &ValidationContext) -> Result<(), ValidationError> {
537 if context.metrics.avg_duration > Duration::from_micros(100) {
538 return Err(ValidationError {
539 message: format!(
540 "High tracking latency: {:.2}µs",
541 context.metrics.avg_duration.as_micros()
542 ),
543 suggestion: Some("Optimize allocation tracking path for lower latency".to_string()),
544 location: Some(context.operation_name.clone()),
545 });
546 }
547
548 Ok(())
549}
550
551fn validate_symbol_performance(context: &ValidationContext) -> Result<(), ValidationError> {
552 if context.operation_name.contains("symbol")
553 && context.metrics.avg_duration > Duration::from_millis(10)
554 {
555 return Err(ValidationError {
556 message: format!(
557 "Slow symbol resolution: {:.2}ms",
558 context.metrics.avg_duration.as_millis()
559 ),
560 suggestion: Some("Consider symbol caching or preloading".to_string()),
561 location: Some(context.operation_name.clone()),
562 });
563 }
564
565 Ok(())
566}
567
568fn validate_error_handling(context: &ValidationContext) -> Result<(), ValidationError> {
569 let coverage_ratio = if context.error_handling.error_points > 0 {
570 context.error_handling.handled_error_points as f64
571 / context.error_handling.error_points as f64
572 } else {
573 1.0
574 };
575
576 if coverage_ratio < 0.9 {
577 return Err(ValidationError {
578 message: format!(
579 "Low error handling coverage: {:.1}%",
580 coverage_ratio * 100.0
581 ),
582 suggestion: Some("Add error handling for unhandled error points".to_string()),
583 location: Some(context.operation_name.clone()),
584 });
585 }
586
587 if !context.error_handling.has_recovery && context.operation_name.contains("critical") {
588 return Err(ValidationError {
589 message: "Critical operation lacks recovery mechanism".to_string(),
590 suggestion: Some("Implement recovery strategies for critical operations".to_string()),
591 location: Some(context.operation_name.clone()),
592 });
593 }
594
595 Ok(())
596}
597
598fn validate_thread_safety(context: &ValidationContext) -> Result<(), ValidationError> {
599 if context.thread_safety.shared_resources > 0 && !context.thread_safety.is_thread_safe {
600 return Err(ValidationError {
601 message: "Operation accesses shared resources without thread safety".to_string(),
602 suggestion: Some("Add proper synchronization for shared resource access".to_string()),
603 location: Some(context.operation_name.clone()),
604 });
605 }
606
607 if context.thread_safety.contention_level > 0.3 {
608 return Err(ValidationError {
609 message: format!(
610 "High lock contention: {:.1}%",
611 context.thread_safety.contention_level * 100.0
612 ),
613 suggestion: Some(
614 "Consider lock-free alternatives or finer-grained locking".to_string(),
615 ),
616 location: Some(context.operation_name.clone()),
617 });
618 }
619
620 Ok(())
621}
622
623impl Default for ValidationConfig {
624 fn default() -> Self {
625 Self {
626 fail_fast: false,
627 max_validation_time: Duration::from_secs(10),
628 enable_deep_checks: true,
629 min_severity: ValidationSeverity::Info,
630 }
631 }
632}
633
634impl Default for QualityValidator {
635 fn default() -> Self {
636 Self::new()
637 }
638}
639
640#[cfg(test)]
641mod tests {
642 use super::*;
643
644 #[test]
645 fn test_validator_creation() {
646 let validator = QualityValidator::new();
647 assert!(!validator.rules.is_empty());
648 assert!(validator.rule_stats.is_empty());
649 }
650
651 #[test]
652 fn test_rule_management() {
653 let mut validator = QualityValidator::new();
654 let initial_count = validator.rules.len();
655
656 let custom_rule = ValidationRule {
657 id: "test_rule".to_string(),
658 name: "Test Rule".to_string(),
659 description: "Test rule description".to_string(),
660 category: RuleCategory::CodeStyle,
661 severity: ValidationSeverity::Info,
662 enabled: true,
663 validator: |_| Ok(()),
664 };
665
666 validator.add_rule(custom_rule);
667 assert_eq!(validator.rules.len(), initial_count + 1);
668
669 assert!(validator.remove_rule("test_rule"));
670 assert_eq!(validator.rules.len(), initial_count);
671 assert!(!validator.remove_rule("nonexistent_rule"));
672 }
673
674 #[test]
675 fn test_validation_context() {
676 let context = ValidationContext {
677 operation_name: "test_operation".to_string(),
678 metrics: OperationMetrics {
679 avg_duration: Duration::from_micros(50),
680 peak_memory: 1024,
681 success_rate: 0.95,
682 allocation_count: 100,
683 cpu_usage: 5.0,
684 },
685 memory_info: MemoryInfo {
686 current_usage: 1024 * 1024,
687 peak_usage: 2 * 1024 * 1024,
688 active_allocations: 100,
689 fragmentation_ratio: 0.1,
690 growth_rate: 0.0,
691 },
692 error_handling: ErrorHandlingInfo {
693 has_error_handling: true,
694 error_points: 10,
695 handled_error_points: 9,
696 has_recovery: true,
697 },
698 thread_safety: ThreadSafetyInfo {
699 is_thread_safe: true,
700 shared_resources: 2,
701 has_synchronization: true,
702 contention_level: 0.1,
703 },
704 };
705
706 let mut validator = QualityValidator::new();
707 let result = validator.validate(&context);
708
709 assert!(matches!(
710 result.status,
711 ValidationStatus::Passed | ValidationStatus::WarningsFound
712 ));
713 assert!(result.summary.quality_score >= 0.0);
714 assert!(result.summary.quality_score <= 1.0);
715 }
716}