#[cfg(feature = "compression-gzip")]
use super::algorithms::GzipCompressor;
use super::algorithms::{CompressionAlgorithm, NoCompressor};
use super::traits::Compressor;
use crate::common::error::Result;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
lazy_static::lazy_static! {
static ref COMPRESSION_REGISTRY: CompressionRegistry = {
let registry = CompressionRegistry::new();
registry.register_defaults();
registry
};
}
pub struct CompressionRegistry {
compressors: Arc<RwLock<HashMap<String, Arc<dyn Compressor>>>>,
}
impl CompressionRegistry {
pub fn new() -> Self {
Self {
compressors: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn register_defaults(&self) {
self.register("none", Arc::new(NoCompressor));
#[cfg(feature = "compression-gzip")]
self.register("gzip", Arc::new(GzipCompressor));
}
pub fn register(&self, name: &str, compressor: Arc<dyn Compressor>) {
if let Ok(mut compressors) = self.compressors.write() {
compressors.insert(name.to_string(), compressor);
}
}
pub fn find(&self, name: &str) -> Option<Arc<dyn Compressor>> {
self.compressors
.read()
.ok()
.and_then(|compressors| compressors.get(name).map(Arc::clone))
}
pub fn find_by_algorithm(
&self,
algorithm: CompressionAlgorithm,
) -> Option<Arc<dyn Compressor>> {
self.find(&algorithm.as_str())
}
pub fn is_registered(&self, name: &str) -> bool {
self.compressors
.read()
.map(|compressors| compressors.contains_key(name))
.unwrap_or(false)
}
pub fn auto_detect(&self, data: &[u8]) -> Option<Arc<dyn Compressor>> {
self.compressors.read().ok().and_then(|compressors| {
compressors
.values()
.find(|compressor| compressor.can_detect(data))
.map(Arc::clone)
})
}
pub fn global() -> &'static CompressionRegistry {
&COMPRESSION_REGISTRY
}
}
impl Default for CompressionRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct CompressionUtil;
impl CompressionUtil {
pub fn get_compressor(algorithm: CompressionAlgorithm) -> Arc<dyn Compressor> {
CompressionRegistry::global()
.find_by_algorithm(algorithm)
.unwrap_or_else(|| Arc::new(NoCompressor))
}
pub fn get_compressor_by_name(name: &str) -> Option<Arc<dyn Compressor>> {
CompressionRegistry::global().find(name)
}
pub fn compress(data: &[u8], algorithm: CompressionAlgorithm) -> Result<Vec<u8>> {
let compressor = Self::get_compressor(algorithm);
compressor.compress(data)
}
pub fn decompress(data: &[u8], algorithm: CompressionAlgorithm) -> Result<Vec<u8>> {
let compressor = Self::get_compressor(algorithm);
compressor.decompress(data)
}
pub fn is_registered(name: &str) -> bool {
CompressionRegistry::global().is_registered(name)
}
pub fn auto_decompress(data: &[u8]) -> Result<(Vec<u8>, CompressionAlgorithm)> {
if let Some(compressor) = CompressionRegistry::global().auto_detect(data) {
let decompressed = compressor.decompress(data)?;
return Ok((decompressed, compressor.algorithm()));
}
Ok((data.to_vec(), CompressionAlgorithm::None))
}
pub fn register_custom(compressor: Arc<dyn Compressor>) {
CompressionRegistry::global().register(compressor.name(), compressor);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compression_registry() {
let registry = CompressionRegistry::new();
registry.register_defaults();
assert!(registry.find("none").is_some());
#[cfg(feature = "compression-gzip")]
assert!(registry.find("gzip").is_some());
#[cfg(not(feature = "compression-gzip"))]
assert!(registry.find("gzip").is_none());
assert!(registry.find("unknown").is_none());
}
#[test]
#[cfg(feature = "compression-gzip")]
fn test_auto_detect() {
let data = b"\x1f\x8b\x08test"; let registry = CompressionRegistry::new();
registry.register_defaults();
let compressor = registry.auto_detect(data);
assert!(compressor.is_some());
assert_eq!(compressor.unwrap().algorithm(), CompressionAlgorithm::Gzip);
}
#[test]
#[cfg(not(feature = "compression-gzip"))]
fn test_auto_detect_without_gzip_feature() {
let data = b"\x1f\x8b\x08test"; let registry = CompressionRegistry::new();
registry.register_defaults();
assert!(registry.auto_detect(data).is_none());
}
}