diff --git a/src/compression.rs b/src/compression.rs index 2ed52f0..ef357a8 100755 --- a/src/compression.rs +++ b/src/compression.rs @@ -10,11 +10,13 @@ use enum_map::{EnumMap,Enum}; pub mod none; pub mod lz4; +pub mod gzip; pub mod program; use none::CompressionEngineNone; use lz4::CompressionEngineLZ4; use program::CompressionProgram; +use gzip::CompressionEngineGZip; #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString, Enum)] #[strum(ascii_case_insensitive)] @@ -38,7 +40,7 @@ pub trait CompressionEngine { lazy_static! { pub static ref COMPRESSION_PROGRAMS: EnumMap> = enum_map! { CompressionType::LZ4 => None, - CompressionType::GZip => Some(CompressionProgram::new("gzip", vec!["-qcf"], vec!["-dcf"])), + CompressionType::GZip => None, CompressionType::BZip2 => Some(CompressionProgram::new("bzip2", vec!["-qcf"], vec!["-dcf"])), CompressionType::XZ => Some(CompressionProgram::new("xz", vec!["-qcf"], vec!["-dcf"])), CompressionType::ZStd => Some(CompressionProgram::new("zstd", vec!["-qcf"], vec!["-dcf"])), @@ -49,8 +51,9 @@ lazy_static! { pub fn get_engine(compression_type: CompressionType) -> Result> { match compression_type { - CompressionType::None => Ok(Box::new(CompressionEngineNone::new())), CompressionType::LZ4 => Ok(Box::new(CompressionEngineLZ4::new())), + CompressionType::GZip => Ok(Box::new(CompressionEngineGZip::new())), + CompressionType::None => Ok(Box::new(CompressionEngineNone::new())), compression_type => Ok(Box::new(COMPRESSION_PROGRAMS[compression_type.clone()].clone().unwrap())) } } diff --git a/src/compression/gzip.rs b/src/compression/gzip.rs new file mode 100644 index 0000000..b352eaa --- /dev/null +++ b/src/compression/gzip.rs @@ -0,0 +1,80 @@ +use anyhow::Result; +use std::fs::File; +use std::io; +use std::io::Write; +use std::path::PathBuf; +use log::*; + +use flate2::Compression; +use flate2::read::GzDecoder; +use flate2::write::GzEncoder; + +use crate::compression::CompressionEngine; + +#[derive(Debug, Eq, PartialEq, Clone, Default)] +pub struct CompressionEngineGZip {} + +impl CompressionEngineGZip { + pub fn new() -> CompressionEngineGZip { + CompressionEngineGZip {} + } +} + +impl CompressionEngine for CompressionEngineGZip { + fn is_supported(&self) -> bool { + true + } + + fn cat(&self, file_path: PathBuf) -> Result<()> { + debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); + + let mut stdout = io::stdout().lock(); + let file = File::open(file_path)?; + let mut gzip_read = GzDecoder::new(file); + + io::copy(&mut gzip_read, &mut stdout)?; + stdout.flush()?; + + Ok(()) + } + + fn create(&self, file_path: PathBuf) -> Result> { + debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); + + let file = File::create(file_path)?; + let gzip_write = GzEncoder::new(file, Compression::default()); + + Ok(Box::new(AutoFinishGzEncoder::new(gzip_write))) + } +} + +pub struct AutoFinishGzEncoder { + encoder: Option>, +} + +impl AutoFinishGzEncoder { + fn new(gz_encoder: GzEncoder) -> AutoFinishGzEncoder { + AutoFinishGzEncoder { + encoder: Some(gz_encoder), + } + } +} + +impl Drop for AutoFinishGzEncoder { + fn drop(&mut self) { + if let Some(encoder) = self.encoder.take() { + debug!("COMPRESSION: Finishing"); + let _ = encoder.finish(); + } + } +} + +impl Write for AutoFinishGzEncoder { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.encoder.as_mut().unwrap().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.encoder.as_mut().unwrap().flush() + } +}