use std::io::{ Read, Write };
use flate2::{ Compression as FlateLevel, read::{ GzDecoder, DeflateDecoder }, write::{ GzEncoder, DeflateEncoder } };
use crate::error::Error;
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum CompressionAlgorithm
{
Gzip,
Deflate,
Brotli,
None,
}
impl CompressionAlgorithm
{
#[ must_use ]
pub fn content_encoding( &self ) -> Option< &'static str >
{
match self
{
Self::Gzip => Some( "gzip" ),
Self::Deflate => Some( "deflate" ),
Self::Brotli => Some( "br" ),
Self::None => None,
}
}
}
impl Default for CompressionAlgorithm
{
#[ inline ]
fn default() -> Self
{
Self::Gzip
}
}
#[ derive( Debug, Clone ) ]
pub struct CompressionConfig
{
pub algorithm : CompressionAlgorithm,
pub level : u32,
pub min_size : usize,
}
impl CompressionConfig
{
#[ must_use ]
pub fn new() -> Self
{
Self {
algorithm : CompressionAlgorithm::Gzip,
level : 6,
min_size : 1024,
}
}
#[ must_use ]
pub fn algorithm( mut self, algorithm : CompressionAlgorithm ) -> Self
{
self.algorithm = algorithm;
self
}
#[ must_use ]
pub fn level( mut self, level : u32 ) -> Self
{
self.level = level;
self
}
#[ must_use ]
pub fn min_size( mut self, min_size : usize ) -> Self
{
self.min_size = min_size;
self
}
}
impl Default for CompressionConfig
{
#[ inline ]
fn default() -> Self
{
Self::new()
}
}
pub fn compress( data : &[ u8 ], config : &CompressionConfig ) -> Result< Vec< u8 >, Error >
{
if data.len() < config.min_size
{
return Ok( data.to_vec() );
}
match config.algorithm
{
CompressionAlgorithm::Gzip => {
let mut encoder = GzEncoder::new( Vec::new(), FlateLevel::new( config.level ) );
encoder.write_all( data )
.map_err( | e | Error::ConfigurationError( format!( "Gzip compression failed : {}", e ) ) )?;
encoder.finish()
.map_err( | e | Error::ConfigurationError( format!( "Gzip compression finish failed : {}", e ) ) )
},
CompressionAlgorithm::Deflate => {
let mut encoder = DeflateEncoder::new( Vec::new(), FlateLevel::new( config.level ) );
encoder.write_all( data )
.map_err( | e | Error::ConfigurationError( format!( "Deflate compression failed : {}", e ) ) )?;
encoder.finish()
.map_err( | e | Error::ConfigurationError( format!( "Deflate compression finish failed : {}", e ) ) )
},
CompressionAlgorithm::Brotli => {
use brotli::enc::BrotliEncoderParams;
let params = BrotliEncoderParams {
quality : config.level as i32,
..Default::default()
};
let mut output = Vec::new();
{
let mut compressor = brotli::CompressorWriter::with_params(
&mut output,
4096,
¶ms
);
compressor.write_all( data )
.map_err( | e | Error::ConfigurationError( format!( "Brotli compression failed : {}", e ) ) )?;
}
Ok( output )
},
CompressionAlgorithm::None => Ok( data.to_vec() ),
}
}
pub fn decompress( data : &[ u8 ], algorithm : CompressionAlgorithm ) -> Result< Vec< u8 >, Error >
{
match algorithm
{
CompressionAlgorithm::Gzip => {
let mut decoder = GzDecoder::new( data );
let mut output = Vec::new();
decoder.read_to_end( &mut output )
.map_err( | e | Error::ConfigurationError( format!( "Gzip decompression failed : {}", e ) ) )?;
Ok( output )
},
CompressionAlgorithm::Deflate => {
let mut decoder = DeflateDecoder::new( data );
let mut output = Vec::new();
decoder.read_to_end( &mut output )
.map_err( | e | Error::ConfigurationError( format!( "Deflate decompression failed : {}", e ) ) )?;
Ok( output )
},
CompressionAlgorithm::Brotli => {
let mut output = Vec::new();
{
let mut decompressor = brotli::Decompressor::new( data, 4096 );
decompressor.read_to_end( &mut output )
.map_err( | e | Error::ConfigurationError( format!( "Brotli decompression failed : {}", e ) ) )?;
}
Ok( output )
},
CompressionAlgorithm::None => Ok( data.to_vec() ),
}
}