use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompressionMetadata {
pub codec: String,
pub original_size: usize,
pub compressed_size: usize,
pub compression_ratio: f64,
pub space_savings: f64,
pub compression_level: Option<i32>,
pub parameters: serde_json::Value,
pub timestamp: SystemTime,
pub duration: Option<Duration>,
pub throughput: Option<f64>,
pub checksum_original: Option<Vec<u8>>,
pub checksum_compressed: Option<Vec<u8>>,
pub extra: serde_json::Value,
}
impl CompressionMetadata {
pub fn new(codec: String, original_size: usize, compressed_size: usize) -> Self {
let compression_ratio = if compressed_size > 0 {
original_size as f64 / compressed_size as f64
} else {
0.0
};
let space_savings = if original_size > 0 {
(1.0 - (compressed_size as f64 / original_size as f64)) * 100.0
} else {
0.0
};
Self {
codec,
original_size,
compressed_size,
compression_ratio,
space_savings,
compression_level: None,
parameters: serde_json::Value::Null,
timestamp: SystemTime::now(),
duration: None,
throughput: None,
checksum_original: None,
checksum_compressed: None,
extra: serde_json::Value::Null,
}
}
pub fn with_level(mut self, level: i32) -> Self {
self.compression_level = Some(level);
self
}
pub fn with_parameters(mut self, parameters: serde_json::Value) -> Self {
self.parameters = parameters;
self
}
pub fn with_duration(mut self, duration: Duration) -> Self {
if self.original_size > 0 {
let seconds = duration.as_secs_f64();
if seconds > 0.0 {
self.throughput = Some((self.original_size as f64 / seconds) / 1_048_576.0);
}
}
self.duration = Some(duration);
self
}
pub fn with_checksums(mut self, original: Vec<u8>, compressed: Vec<u8>) -> Self {
self.checksum_original = Some(original);
self.checksum_compressed = Some(compressed);
self
}
pub fn with_extra(mut self, extra: serde_json::Value) -> Self {
self.extra = extra;
self
}
pub fn format_summary(&self) -> String {
format!(
"Codec: {}, Size: {} -> {} bytes ({:.2}x ratio, {:.1}% savings), Throughput: {}",
self.codec,
self.original_size,
self.compressed_size,
self.compression_ratio,
self.space_savings,
self.throughput
.map(|t| format!("{:.2} MB/s", t))
.unwrap_or_else(|| "N/A".to_string())
)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CompressionStats {
pub operations: usize,
pub total_original_size: usize,
pub total_compressed_size: usize,
pub avg_compression_ratio: f64,
pub avg_space_savings: f64,
pub avg_throughput: f64,
pub min_compression_ratio: f64,
pub max_compression_ratio: f64,
pub total_duration: Duration,
}
impl CompressionStats {
pub fn new() -> Self {
Self {
operations: 0,
total_original_size: 0,
total_compressed_size: 0,
avg_compression_ratio: 0.0,
avg_space_savings: 0.0,
avg_throughput: 0.0,
min_compression_ratio: f64::MAX,
max_compression_ratio: 0.0,
total_duration: Duration::ZERO,
}
}
pub fn add_metadata(&mut self, metadata: &CompressionMetadata) {
self.operations += 1;
self.total_original_size += metadata.original_size;
self.total_compressed_size += metadata.compressed_size;
self.min_compression_ratio = self.min_compression_ratio.min(metadata.compression_ratio);
self.max_compression_ratio = self.max_compression_ratio.max(metadata.compression_ratio);
if self.total_compressed_size > 0 {
self.avg_compression_ratio =
self.total_original_size as f64 / self.total_compressed_size as f64;
}
if self.total_original_size > 0 {
self.avg_space_savings = (1.0
- (self.total_compressed_size as f64 / self.total_original_size as f64))
* 100.0;
}
if let Some(duration) = metadata.duration {
self.total_duration += duration;
let seconds = self.total_duration.as_secs_f64();
if seconds > 0.0 {
self.avg_throughput = (self.total_original_size as f64 / seconds) / 1_048_576.0;
}
}
}
}