use std::io::{self, Read};
use flate2::bufread::{
DeflateDecoder, DeflateEncoder, GzDecoder, GzEncoder, ZlibDecoder, ZlibEncoder,
};
use flate2::Compression;
use serde::{Deserialize, Serialize};
use super::{Codec, Frame};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum CompressionLevel {
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
}
impl CompressionLevel {
pub const BEST: Self = Self::Nine;
pub const FAST: Self = Self::One;
pub const NONE: Self = Self::Zero;
}
impl Default for CompressionLevel {
fn default() -> Self {
Self::Six
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CompressionType {
Deflate,
Gzip,
Zlib,
#[serde(other)]
Unknown,
}
impl CompressionType {
pub const fn known_variants() -> &'static [CompressionType] {
&[
CompressionType::Deflate,
CompressionType::Gzip,
CompressionType::Zlib,
]
}
pub fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
pub fn new_codec(&self, level: CompressionLevel) -> io::Result<CompressionCodec> {
CompressionCodec::from_type_and_level(*self, level)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CompressionCodec {
Deflate { level: CompressionLevel },
Gzip { level: CompressionLevel },
Zlib { level: CompressionLevel },
}
impl CompressionCodec {
pub fn from_type_and_level(
ty: CompressionType,
level: CompressionLevel,
) -> io::Result<CompressionCodec> {
match ty {
CompressionType::Deflate => Ok(Self::Deflate { level }),
CompressionType::Gzip => Ok(Self::Gzip { level }),
CompressionType::Zlib => Ok(Self::Zlib { level }),
CompressionType::Unknown => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Unknown compression type",
)),
}
}
pub fn deflate(level: impl Into<CompressionLevel>) -> Self {
Self::Deflate {
level: level.into(),
}
}
pub fn gzip(level: impl Into<CompressionLevel>) -> Self {
Self::Gzip {
level: level.into(),
}
}
pub fn zlib(level: impl Into<CompressionLevel>) -> Self {
Self::Zlib {
level: level.into(),
}
}
pub fn level(&self) -> CompressionLevel {
match self {
Self::Deflate { level } => *level,
Self::Gzip { level } => *level,
Self::Zlib { level } => *level,
}
}
pub fn ty(&self) -> CompressionType {
match self {
Self::Deflate { .. } => CompressionType::Deflate,
Self::Gzip { .. } => CompressionType::Gzip,
Self::Zlib { .. } => CompressionType::Zlib,
}
}
}
impl Codec for CompressionCodec {
fn encode<'a>(&mut self, frame: Frame<'a>) -> io::Result<Frame<'a>> {
let item = frame.as_item();
let mut buf = Vec::new();
match *self {
Self::Deflate { level } => {
DeflateEncoder::new(item, Compression::new(level as u32)).read_to_end(&mut buf)?
}
Self::Gzip { level } => {
GzEncoder::new(item, Compression::new(level as u32)).read_to_end(&mut buf)?
}
Self::Zlib { level } => {
ZlibEncoder::new(item, Compression::new(level as u32)).read_to_end(&mut buf)?
}
};
Ok(Frame::from(buf))
}
fn decode<'a>(&mut self, frame: Frame<'a>) -> io::Result<Frame<'a>> {
let item = frame.as_item();
let mut buf = Vec::new();
match *self {
Self::Deflate { .. } => DeflateDecoder::new(item).read_to_end(&mut buf)?,
Self::Gzip { .. } => GzDecoder::new(item).read_to_end(&mut buf)?,
Self::Zlib { .. } => ZlibDecoder::new(item).read_to_end(&mut buf)?,
};
Ok(Frame::from(buf))
}
}
#[cfg(test)]
mod tests {
use test_log::test;
use super::*;
#[test]
fn encode_should_apply_appropriate_compression_algorithm() {
let mut codec = CompressionCodec::deflate(CompressionLevel::BEST);
let frame = codec.encode(Frame::new(b"some bytes")).unwrap();
let mut item = Vec::new();
DeflateDecoder::new(frame.as_item())
.read_to_end(&mut item)
.unwrap();
assert_eq!(item, b"some bytes");
let mut codec = CompressionCodec::gzip(CompressionLevel::BEST);
let frame = codec.encode(Frame::new(b"some bytes")).unwrap();
let mut item = Vec::new();
GzDecoder::new(frame.as_item())
.read_to_end(&mut item)
.unwrap();
assert_eq!(item, b"some bytes");
let mut codec = CompressionCodec::zlib(CompressionLevel::BEST);
let frame = codec.encode(Frame::new(b"some bytes")).unwrap();
let mut item = Vec::new();
ZlibDecoder::new(frame.as_item())
.read_to_end(&mut item)
.unwrap();
assert_eq!(item, b"some bytes");
}
#[test]
fn decode_should_apply_appropriate_decompression_algorithm() {
let frame = {
let mut item = Vec::new();
DeflateEncoder::new(b"some bytes".as_slice(), Compression::best())
.read_to_end(&mut item)
.unwrap();
Frame::from(item)
};
let mut codec = CompressionCodec::deflate(CompressionLevel::BEST);
let frame = codec.decode(frame).unwrap();
assert_eq!(frame, b"some bytes");
let frame = {
let mut item = Vec::new();
GzEncoder::new(b"some bytes".as_slice(), Compression::best())
.read_to_end(&mut item)
.unwrap();
Frame::from(item)
};
let mut codec = CompressionCodec::gzip(CompressionLevel::BEST);
let frame = codec.decode(frame).unwrap();
assert_eq!(frame, b"some bytes");
let frame = {
let mut item = Vec::new();
ZlibEncoder::new(b"some bytes".as_slice(), Compression::best())
.read_to_end(&mut item)
.unwrap();
Frame::from(item)
};
let mut codec = CompressionCodec::zlib(CompressionLevel::BEST);
let frame = codec.decode(frame).unwrap();
assert_eq!(frame, b"some bytes");
}
}