use super::{VfsCompression, VfsCompressionConfig, VfsError}; use std::path::Path; pub struct Compressor { config: VfsCompressionConfig, } impl Compressor { pub fn new(config: VfsCompressionConfig) -> Self { Self { config } } pub fn should_compress(&self, size: u64) -> bool { self.config.algorithm != VfsCompression::None && size >= self.config.min_size } pub fn compress(&self, data: &[u8]) -> Result, VfsError> { if !self.should_compress(data.len() as u64) { return Ok(data.to_vec()); } match self.config.algorithm { VfsCompression::None => Ok(data.to_vec()), VfsCompression::Zstd => { let level = self.config.level as i32; zstd::encode_all(data, level) .map_err(|e| VfsError::Io(format!("ZSTD compression failed: {}", e))) } VfsCompression::Lz4 => { Ok(lz4_flex::compress_prepend_size(data)) } } } pub fn decompress(&self, data: &[u8]) -> Result, VfsError> { match self.config.algorithm { VfsCompression::None => Ok(data.to_vec()), VfsCompression::Zstd => { zstd::decode_all(data) .map_err(|e| VfsError::Io(format!("ZSTD decompression failed: {}", e))) } VfsCompression::Lz4 => { lz4_flex::decompress_size_prepended(data) .map_err(|e| VfsError::Io(format!("LZ4 decompression failed: {}", e))) } } } pub fn compress_file(&self, source: &Path, target: &Path) -> Result<(), VfsError> { let data = std::fs::read(source) .map_err(|e| super::util::map_io_error(source, e))?; if !self.should_compress(data.len() as u64) { std::fs::copy(source, target) .map_err(|e| super::util::map_io_error(source, e))?; return Ok(()); } let compressed = self.compress(&data)?; std::fs::write(target, compressed) .map_err(|e| super::util::map_io_error(target, e))?; Ok(()) } pub fn decompress_file(&self, source: &Path, target: &Path) -> Result<(), VfsError> { let data = std::fs::read(source) .map_err(|e| super::util::map_io_error(source, e))?; let decompressed = self.decompress(&data)?; std::fs::write(target, decompressed) .map_err(|e| super::util::map_io_error(target, e))?; Ok(()) } pub fn extension(&self) -> &'static str { match self.config.algorithm { VfsCompression::None => "", VfsCompression::Zstd => ".zst", VfsCompression::Lz4 => ".lz4", } } } pub fn detect_compression(path: &Path) -> VfsCompression { let ext = path.extension().map(|e| e.to_string_lossy()); match ext.as_ref().map(|s| s.as_ref()) { Some("zst") => VfsCompression::Zstd, Some("lz4") => VfsCompression::Lz4, _ => VfsCompression::None, } } pub fn get_decompressed_size(path: &Path) -> Result { let data = std::fs::read(path) .map_err(|e| super::util::map_io_error(path, e))?; match detect_compression(path) { VfsCompression::Zstd => { let decompressed = zstd::decode_all(data.as_slice()) .map_err(|e| VfsError::Io(format!("ZSTD decompression failed: {}", e)))?; Ok(decompressed.len() as u64) } VfsCompression::Lz4 => { Err(VfsError::Unsupported("LZ4 size detection not implemented".to_string())) } VfsCompression::None => Ok(data.len() as u64), } }