use crate::compression_engine::{get_compression_engine, CompressionType}; use crate::services::error::CoreError; use std::io::Read; use std::path::PathBuf; use std::str::FromStr; use anyhow::anyhow; //// Service for handling compression and decompression of item content. /// /// Provides methods to read compressed item files either fully into memory /// or as streaming readers. Supports various compression types via engines. /// This service abstracts the underlying compression engines for consistent access. /// /// # Examples /// /// ``` /// let service = CompressionService::new(); /// let content = service.get_item_content(path, "gzip")?; /// ``` pub struct CompressionService; /// Service for handling compression and decompression of item content. /// /// Provides methods to read compressed item files either fully into memory /// or as streaming readers. Supports various compression types via engines. /// /// # Examples /// /// ``` /// let service = CompressionService::new(); /// let content = service.get_item_content(path, "gzip")?; /// ``` impl CompressionService { /// Creates a new CompressionService instance. /// /// This is a simple constructor; no initialization is required beyond the static methods. /// /// # Returns /// /// * `CompressionService` - A new instance of the service. /// /// # Examples /// /// ``` /// let service = CompressionService::new(); /// ``` pub fn new() -> Self { Self } /// Reads and decompresses the full content of an item file into memory. /// /// Loads the entire decompressed content as a byte vector. Suitable for small to medium files. /// /// # Arguments /// /// * `item_path` - Path to the compressed item file on disk. /// * `compression` - Compression type as string (e.g., "gzip", "lz4"); case-insensitive. /// /// # Returns /// /// * `Ok(Vec)` - The full decompressed content bytes. /// * `Err(CoreError)` - On failure (see errors). /// /// # Errors /// /// * `CoreError::Compression(String)` - If the compression type string is invalid. /// * `CoreError::Other(anyhow::Error)` - If the file cannot be opened, the engine fails, or reading encounters an I/O error. /// /// # Examples /// /// ``` /// let content = service.get_item_content(item_path, "lz4")?; /// assert_eq!(content.len(), expected_size); /// ``` pub fn get_item_content(&self, item_path: PathBuf, compression: &str) -> Result, CoreError> { let compression_type = CompressionType::from_str(compression) .map_err(|e| CoreError::Compression(e.to_string()))?; let engine = get_compression_engine(compression_type) .map_err(|e| CoreError::Other(anyhow!(e.to_string())))?; let mut reader = engine.open(item_path.clone()) .map_err(|e| CoreError::Other(anyhow!("Failed to open item file {:?}: {}", item_path, e)))?; let mut content = Vec::new(); reader.read_to_end(&mut content)?; Ok(content) } /// Opens a streaming reader for decompressing item content. /// /// Due to Send requirements in async contexts, this loads the full content into a Cursor. /// Warning: For very large files, this consumes significant memory; consider alternatives for streaming without loading all data. /// /// # Arguments /// /// * `item_path` - Path to the compressed item file on disk. /// * `compression` - Compression type as string (e.g., "gzip", "lz4"); case-insensitive. /// /// # Returns /// /// * `Ok(Box)` - A boxed reader that can be used for streaming decompressed data. /// * `Err(CoreError)` - On failure (see errors). /// /// # Errors /// /// * `CoreError::Compression(String)` - If the compression type string is invalid. /// * `CoreError::Other(anyhow::Error)` - If the file cannot be opened, the engine fails, or reading encounters an I/O error. /// /// # Examples /// /// ``` /// let mut reader = service.stream_item_content(item_path, "gzip")?; /// let mut buf = [0; 1024]; /// let n = reader.read(&mut buf)?; /// ``` pub fn stream_item_content( &self, item_path: PathBuf, compression: &str, ) -> Result, CoreError> { let compression_type = CompressionType::from_str(compression) .map_err(|e| CoreError::Compression(e.to_string()))?; let engine = get_compression_engine(compression_type) .map_err(|e| CoreError::Other(anyhow!(e.to_string())))?; let reader = engine.open(item_path.clone()) .map_err(|e| CoreError::Other(anyhow!("Failed to open item file {:?}: {}", item_path, e)))?; // Since we can't guarantee the reader implements Send, we need to wrap it // We'll read the content into a buffer and return a Cursor which is Send // This is not ideal for large files, but it ensures Send is implemented let mut content = Vec::new(); let mut temp_reader = reader; temp_reader.read_to_end(&mut content)?; Ok(Box::new(std::io::Cursor::new(content))) } } impl Default for CompressionService { fn default() -> Self { Self::new() } }