use crate::codec::CodecMethod;
#[derive(Debug, Clone, Default)]
pub struct EncryptionInfo {
pub key_derivation_iterations: u64,
pub num_cycles_power: u8,
pub salt_size: usize,
pub iv_size: usize,
}
impl EncryptionInfo {
pub fn new(num_cycles_power: u8, salt_size: usize, iv_size: usize) -> Self {
Self {
key_derivation_iterations: 1u64 << num_cycles_power,
num_cycles_power,
salt_size,
iv_size,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ArchiveInfo {
pub entry_count: usize,
pub total_size: u64,
pub packed_size: u64,
pub is_solid: bool,
pub has_encrypted_entries: bool,
pub has_encrypted_header: bool,
pub compression_methods: Vec<CodecMethod>,
pub folder_count: usize,
pub comment: Option<String>,
pub encryption_info: Option<EncryptionInfo>,
}
impl ArchiveInfo {
pub fn compression_ratio(&self) -> f64 {
if self.total_size == 0 {
1.0
} else {
self.packed_size as f64 / self.total_size as f64
}
}
pub fn space_savings(&self) -> f64 {
if self.total_size == 0 {
0.0
} else {
1.0 - self.compression_ratio()
}
}
pub fn comment(&self) -> Option<&str> {
self.comment.as_deref()
}
}
#[must_use = "test results should be checked to verify archive integrity"]
#[derive(Debug, Clone, Default)]
pub struct TestResult {
pub entries_tested: usize,
pub entries_passed: usize,
pub entries_failed: usize,
pub failures: Vec<(String, String)>,
}
impl TestResult {
pub fn is_ok(&self) -> bool {
self.entries_failed == 0
}
pub fn is_err(&self) -> bool {
self.entries_failed > 0
}
}
#[must_use = "extraction results should be checked for warnings or partial failures"]
#[derive(Debug, Clone, Default)]
pub struct ExtractResult {
pub entries_extracted: usize,
pub entries_skipped: usize,
pub entries_failed: usize,
pub bytes_extracted: u64,
pub failures: Vec<(String, String)>,
}
impl ExtractResult {
pub fn is_ok(&self) -> bool {
self.entries_failed == 0
}
pub fn is_err(&self) -> bool {
self.entries_failed > 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_archive_info_compression_ratio() {
let info = ArchiveInfo {
total_size: 1000,
packed_size: 500,
..Default::default()
};
assert!((info.compression_ratio() - 0.5).abs() < 0.001);
assert!((info.space_savings() - 0.5).abs() < 0.001);
}
#[test]
fn test_archive_info_empty() {
let info = ArchiveInfo::default();
assert!((info.compression_ratio() - 1.0).abs() < 0.001);
assert!((info.space_savings() - 0.0).abs() < 0.001);
}
#[test]
fn test_test_result() {
let mut result = TestResult::default();
assert!(result.is_ok());
assert!(!result.is_err());
result.entries_failed = 1;
assert!(!result.is_ok());
assert!(result.is_err());
}
#[test]
fn test_extract_result() {
let mut result = ExtractResult::default();
assert!(result.is_ok());
result.entries_failed = 1;
assert!(!result.is_ok());
assert!(result.is_err());
}
#[test]
fn test_archive_info_comment() {
let info = ArchiveInfo::default();
assert!(info.comment().is_none());
let info = ArchiveInfo {
comment: Some("Test comment".to_string()),
..Default::default()
};
assert_eq!(info.comment(), Some("Test comment"));
}
#[test]
fn test_encryption_info() {
let info = EncryptionInfo::new(19, 8, 16);
assert_eq!(info.num_cycles_power, 19);
assert_eq!(info.key_derivation_iterations, 1u64 << 19); assert_eq!(info.salt_size, 8);
assert_eq!(info.iv_size, 16);
let info = EncryptionInfo::new(0, 0, 0);
assert_eq!(info.key_derivation_iterations, 1);
assert_eq!(info.salt_size, 0);
assert_eq!(info.iv_size, 0);
}
#[test]
fn test_archive_info_with_encryption() {
let encryption_info = EncryptionInfo::new(19, 8, 16);
let info = ArchiveInfo {
has_encrypted_entries: true,
encryption_info: Some(encryption_info),
..Default::default()
};
assert!(info.has_encrypted_entries);
assert!(info.encryption_info.is_some());
let enc = info.encryption_info.unwrap();
assert_eq!(enc.key_derivation_iterations, 524288);
}
}