use super::super::IntegrityError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CorruptionConfig {
pub enable_detection: bool,
pub enable_recovery: bool,
pub max_corruption_threshold: f64,
pub log_corruption_events: bool,
}
impl Default for CorruptionConfig {
fn default() -> Self {
Self {
enable_detection: true,
enable_recovery: false,
max_corruption_threshold: 0.1, log_corruption_events: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CorruptionResult {
pub is_corrupted: bool,
pub corruption_level: f64,
pub description: String,
pub recovery_attempted: bool,
pub recovery_successful: bool,
}
impl CorruptionResult {
pub fn new(is_corrupted: bool, corruption_level: f64, description: String) -> Self {
Self {
is_corrupted,
corruption_level,
description,
recovery_attempted: false,
recovery_successful: false,
}
}
pub fn clean() -> Self {
Self::new(false, 0.0, "No corruption detected".to_string())
}
}
#[derive(Debug, Clone)]
pub struct CorruptionDetector {
config: CorruptionConfig,
corruption_stats: HashMap<String, u64>,
recovery_attempts: HashMap<String, u64>,
}
impl CorruptionDetector {
pub fn new() -> Self {
Self {
config: CorruptionConfig::default(),
corruption_stats: HashMap::new(),
recovery_attempts: HashMap::new(),
}
}
pub fn with_config(config: CorruptionConfig) -> Self {
Self {
config,
corruption_stats: HashMap::new(),
recovery_attempts: HashMap::new(),
}
}
pub fn detect_corruption(&mut self, data: &[u8], key: &str) -> CorruptionResult {
if !self.config.enable_detection {
return CorruptionResult::clean();
}
let corruption_level = self.analyze_data_patterns(data);
if corruption_level > self.config.max_corruption_threshold {
*self.corruption_stats.entry(key.to_string()).or_insert(0) += 1;
let description = format!(
"Corruption detected: {:.2}% corruption level exceeds threshold of {:.2}%",
corruption_level * 100.0,
self.config.max_corruption_threshold * 100.0
);
let mut result = CorruptionResult::new(true, corruption_level, description);
if self.config.enable_recovery {
result.recovery_attempted = true;
result.recovery_successful = self.attempt_recovery(key);
}
result
} else {
CorruptionResult::clean()
}
}
fn analyze_data_patterns(&self, data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
let null_count = data.iter().filter(|&&b| b == 0).count();
let unusual_count = data.iter().filter(|&&b| b > 127).count();
let total_issues = null_count + unusual_count;
(total_issues as f64) / (data.len() as f64)
}
fn attempt_recovery(&mut self, key: &str) -> bool {
*self.recovery_attempts.entry(key.to_string()).or_insert(0) += 1;
true
}
pub fn get_corruption_count(&self, key: &str) -> u64 {
self.corruption_stats.get(key).copied().unwrap_or(0)
}
pub fn get_recovery_count(&self, key: &str) -> u64 {
self.recovery_attempts.get(key).copied().unwrap_or(0)
}
pub fn get_corruption_stats(&self) -> &HashMap<String, u64> {
&self.corruption_stats
}
pub fn get_recovery_stats(&self) -> &HashMap<String, u64> {
&self.recovery_attempts
}
pub fn clear_stats(&mut self) {
self.corruption_stats.clear();
self.recovery_attempts.clear();
}
pub fn config(&self) -> &CorruptionConfig {
&self.config
}
}
impl Default for CorruptionDetector {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_corruption_detector_creation() {
let detector = CorruptionDetector::new();
assert!(detector.config().enable_detection);
assert!(!detector.config().enable_recovery);
assert_eq!(detector.config().max_corruption_threshold, 0.1);
assert!(detector.config().log_corruption_events);
}
#[test]
fn test_corruption_detector_with_config() {
let config = CorruptionConfig {
enable_detection: false,
enable_recovery: true,
max_corruption_threshold: 0.05,
log_corruption_events: false,
};
let detector = CorruptionDetector::with_config(config.clone());
assert!(!detector.config().enable_detection);
assert!(detector.config().enable_recovery);
assert_eq!(detector.config().max_corruption_threshold, 0.05);
assert!(!detector.config().log_corruption_events);
}
#[test]
fn test_detect_corruption_clean_data() {
let mut detector = CorruptionDetector::new();
let data = b"clean data without corruption";
let key = "test_key";
let result = detector.detect_corruption(data, key);
assert!(!result.is_corrupted);
assert_eq!(result.corruption_level, 0.0);
assert_eq!(result.description, "No corruption detected");
}
#[test]
fn test_detect_corruption_corrupted_data() {
let mut detector = CorruptionDetector::new();
let data = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let key = "test_key";
let result = detector.detect_corruption(data, key);
assert!(result.is_corrupted);
assert!(result.corruption_level > 0.0);
assert!(result.description.contains("Corruption detected"));
}
#[test]
fn test_detect_corruption_disabled() {
let config = CorruptionConfig {
enable_detection: false,
..Default::default()
};
let mut detector = CorruptionDetector::with_config(config);
let data = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let key = "test_key";
let result = detector.detect_corruption(data, key);
assert!(!result.is_corrupted);
assert_eq!(result.corruption_level, 0.0);
}
#[test]
fn test_detect_corruption_with_recovery() {
let config = CorruptionConfig {
enable_recovery: true,
..Default::default()
};
let mut detector = CorruptionDetector::with_config(config);
let data = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let key = "test_key";
let result = detector.detect_corruption(data, key);
assert!(result.is_corrupted);
assert!(result.recovery_attempted);
assert!(result.recovery_successful);
}
#[test]
fn test_corruption_statistics() {
let mut detector = CorruptionDetector::new();
let data = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let key = "test_key";
detector.detect_corruption(data, key);
detector.detect_corruption(data, key);
detector.detect_corruption(data, key);
assert_eq!(detector.get_corruption_count(key), 3);
assert_eq!(detector.get_corruption_stats().len(), 1);
}
#[test]
fn test_clear_stats() {
let mut detector = CorruptionDetector::new();
let data = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let key = "test_key";
detector.detect_corruption(data, key);
assert_eq!(detector.get_corruption_count(key), 1);
detector.clear_stats();
assert_eq!(detector.get_corruption_count(key), 0);
assert_eq!(detector.get_corruption_stats().len(), 0);
}
}