bitbottle 0.9.1

a modern archive file format
Documentation
use lzma::{LzmaReader, LzmaWriter};
use num_enum::TryFromPrimitive;
use snap::read::FrameDecoder;
use snap::write::FrameEncoder;
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,
}

/// Abstract compressor that can use a runtime-selected algorithm to compress
/// wrap a `Write`.
pub enum Compressor<W: Write> {
    Snappy(FrameEncoder<W>),
    Lzma2(LzmaWriter<W>),
}

impl<W: Write> Compressor<W> {
    pub fn new(write: W, algorithm: CompressionAlgorithm) -> BottleResult<Compressor<W>> {
        match algorithm {
            CompressionAlgorithm::SNAPPY => Ok(Compressor::Snappy(FrameEncoder::new(write))),
            CompressionAlgorithm::LZMA2 => {
                let encoder = LzmaWriter::new_compressor(write, 9).map_err(BottleError::Lzma2Error)?;
                Ok(Compressor::Lzma2(encoder))
            },
        }
    }

    pub fn close(self) -> BottleResult<W> {
        match self {
            Compressor::Snappy(encoder) => encoder.into_inner().map_err(|_| BottleError::CompressionError),
            Compressor::Lzma2(encoder) => encoder.finish().map_err(BottleError::Lzma2Error),
        }
    }
}

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(())
    }
}


/// Abstract decompressor that can use a runtime-selected algorithm to wrap
/// a `Read`.
pub enum Decompressor<R: Read> {
    Snappy(FrameDecoder<R>),
    Lzma2(LzmaReader<R>),
}

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 => {
                let decoder = LzmaReader::new_decompressor(read).map_err(BottleError::Lzma2Error)?;
                Ok(Decompressor::Lzma2(decoder))
            },
        }
    }
}

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),
        }
    }
}