use super::BLTEArchive;
use crate::Result;
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub file_index: usize,
pub is_valid: bool,
pub error: Option<String>,
pub validation_time_us: u64,
}
#[derive(Debug, Clone, Default)]
pub struct ValidationReport {
pub total_files: usize,
pub valid_files: usize,
pub invalid_files: usize,
pub results: Vec<ValidationResult>,
pub total_time_us: u64,
}
#[derive(Debug, Clone)]
pub struct HeaderValidationReport {
pub total_files: usize,
pub valid_headers: usize,
pub invalid_headers: usize,
pub validation_time_us: u64,
}
#[derive(Debug, Clone)]
pub struct DeepValidationReport {
pub basic: ValidationReport,
pub decompressed_files: usize,
pub decompression_errors: usize,
pub total_decompressed_bytes: u64,
}
impl BLTEArchive {
pub fn validate(&self) -> Result<ValidationReport> {
let start_time = std::time::Instant::now();
let mut report = ValidationReport {
total_files: self.files.len(),
..Default::default()
};
for (index, entry) in self.files.iter().enumerate() {
let result = self.validate_entry(index, entry);
if result.is_valid {
report.valid_files += 1;
} else {
report.invalid_files += 1;
}
report.results.push(result);
}
report.total_time_us = start_time.elapsed().as_micros() as u64;
Ok(report)
}
pub fn validate_headers(&self) -> Result<HeaderValidationReport> {
let start_time = std::time::Instant::now();
let mut valid_headers = 0;
let mut invalid_headers = 0;
for entry in &self.files {
if entry.size > 8 && entry.metadata.compressed_size > 0 {
valid_headers += 1;
} else {
invalid_headers += 1;
}
}
Ok(HeaderValidationReport {
total_files: self.files.len(),
valid_headers,
invalid_headers,
validation_time_us: start_time.elapsed().as_micros() as u64,
})
}
pub fn validate_deep(&mut self) -> Result<DeepValidationReport> {
let basic = self.validate()?;
let mut decompressed_files = 0;
let mut decompression_errors = 0;
let mut total_decompressed_bytes = 0;
for entry in &self.files {
if let Some(ref _blte) = entry.blte {
decompressed_files += 1;
total_decompressed_bytes += entry.size as u64;
} else {
decompression_errors += 1;
}
}
Ok(DeepValidationReport {
basic,
decompressed_files,
decompression_errors,
total_decompressed_bytes,
})
}
pub fn validate_file(&self, index: usize) -> Result<ValidationResult> {
if index >= self.files.len() {
return Ok(ValidationResult {
file_index: index,
is_valid: false,
error: Some(format!("File index {index} out of range")),
validation_time_us: 0,
});
}
let entry = &self.files[index];
Ok(self.validate_entry(index, entry))
}
fn validate_entry(&self, index: usize, entry: &super::ArchiveEntry) -> ValidationResult {
let start_time = std::time::Instant::now();
if entry.size == 0 {
return ValidationResult {
file_index: index,
is_valid: false,
error: Some("Entry has zero size".to_string()),
validation_time_us: start_time.elapsed().as_micros() as u64,
};
}
if entry.metadata.compressed_size == 0 {
return ValidationResult {
file_index: index,
is_valid: false,
error: Some("Entry has zero compressed size".to_string()),
validation_time_us: start_time.elapsed().as_micros() as u64,
};
}
ValidationResult {
file_index: index,
is_valid: true,
error: None,
validation_time_us: start_time.elapsed().as_micros() as u64,
}
}
}
impl ValidationReport {
pub fn is_all_valid(&self) -> bool {
self.invalid_files == 0 && self.total_files > 0
}
pub fn success_rate(&self) -> f64 {
if self.total_files == 0 {
0.0
} else {
(self.valid_files as f64 / self.total_files as f64) * 100.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validation_report_creation() {
let report = ValidationReport::default();
assert_eq!(report.total_files, 0);
assert_eq!(report.valid_files, 0);
assert_eq!(report.invalid_files, 0);
assert!(report.results.is_empty());
}
#[test]
fn test_success_rate_calculation() {
let report = ValidationReport {
total_files: 10,
valid_files: 8,
invalid_files: 2,
..Default::default()
};
assert_eq!(report.success_rate(), 80.0);
assert!(!report.is_all_valid());
}
}