use cfg_if::cfg_if;
use num_enum::TryFromPrimitive;
use snap::read::FrameDecoder;
use snap::write::FrameEncoder;
use std::fmt;
use std::io::{Read, Write};
use crate::{BottleError, BottleResult};
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
pub enum CompressionAlgorithm {
SNAPPY = 0,
LZMA2 = 1,
}
cfg_if! {
if #[cfg(feature = "lzma2")] {
type LzmaLibraryWriter<W> = lzma::LzmaWriter<W>;
type LzmaLibraryReader<R> = lzma::LzmaReader<R>;
} else {
type LzmaLibraryWriter<W> = W;
type LzmaLibraryReader<R> = R;
}
}
pub enum Compressor<W: Write> {
Snappy(Box<FrameEncoder<W>>),
Lzma2(LzmaLibraryWriter<W>),
}
impl<W: Write> Compressor<W> {
pub fn new(write: W, algorithm: CompressionAlgorithm) -> BottleResult<Compressor<W>> {
match algorithm {
CompressionAlgorithm::SNAPPY => Ok(Compressor::Snappy(Box::new(FrameEncoder::new(write)))),
CompressionAlgorithm::LZMA2 => {
cfg_if! {
if #[cfg(feature = "lzma2")] {
let encoder = lzma::LzmaWriter::new_compressor(write, 9).map_err(BottleError::Lzma2Error)?;
Ok(Compressor::Lzma2(encoder))
} else {
Compressor::Lzma2(write);
Err(BottleError::LzmaFeatureMissing)
}
}
},
}
}
pub fn close(self) -> BottleResult<W> {
match self {
Compressor::Snappy(encoder) => encoder.into_inner().map_err(|_| BottleError::CompressionError),
Compressor::Lzma2(encoder) => {
cfg_if! {
if #[cfg(feature = "lzma2")] {
encoder.finish().map_err(BottleError::Lzma2Error)
} else {
let _ = encoder;
Err(BottleError::LzmaFeatureMissing)
}
}
}
}
}
}
impl<W: Write> Write for Compressor<W> {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
match self {
Compressor::Snappy(encoder) => encoder.write(data),
Compressor::Lzma2(encoder) => encoder.write(data),
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub enum Decompressor<R: Read> {
Snappy(FrameDecoder<R>),
Lzma2(LzmaLibraryReader<R>),
}
impl<R: Read> fmt::Debug for Decompressor<R> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Decompressor({})", match self {
Decompressor::Snappy(_) => "snappy",
Decompressor::Lzma2(_) => "lzma2",
})
}
}
impl<R: Read> Decompressor<R> {
pub fn new(read: R, algorithm: CompressionAlgorithm) -> BottleResult<Decompressor<R>> {
match algorithm {
CompressionAlgorithm::SNAPPY => Ok(Decompressor::Snappy(FrameDecoder::new(read))),
CompressionAlgorithm::LZMA2 => {
cfg_if! {
if #[cfg(feature = "lzma2")] {
let decoder = lzma::LzmaReader::new_decompressor(read).map_err(BottleError::Lzma2Error)?;
Ok(Decompressor::Lzma2(decoder))
} else {
Decompressor::Lzma2(read);
Err(BottleError::LzmaFeatureMissing)
}
}
},
}
}
}
impl<R: Read> Read for Decompressor<R> {
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
match self {
Decompressor::Snappy(decoder) => decoder.read(buffer),
Decompressor::Lzma2(decoder) => decoder.read(buffer),
}
}
}