use std::{fmt, io, str::FromStr};
use thiserror::Error;
pub(crate) type CompressionResult<T> = Result<T, CompressionError>;
#[derive(Debug, Error)]
pub(crate) enum CompressionError {
#[error("unsupported compression algorithm: {0}")]
UnsupportedAlgo(String),
#[error("compression failed for {algo}: {source}")]
Compress {
algo: CompressionAlgo,
source: io::Error,
},
#[error("decompression failed for {algo}: {source}")]
Decompress {
algo: CompressionAlgo,
source: io::Error,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CompressionAlgo {
Zstd,
}
impl CompressionAlgo {
const ZSTD: &'static str = "zstd";
pub(crate) const fn as_str(self) -> &'static str {
match self {
Self::Zstd => Self::ZSTD,
}
}
}
impl fmt::Display for CompressionAlgo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for CompressionAlgo {
type Err = CompressionError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
Self::ZSTD => Ok(Self::Zstd),
value => Err(CompressionError::UnsupportedAlgo(value.to_owned())),
}
}
}
pub(crate) trait CompressionCodec {
fn compress(&self, algo: CompressionAlgo, bytes: &[u8]) -> CompressionResult<Vec<u8>>;
fn decompress(
&self,
algo: CompressionAlgo,
stored: StoredCompressedBytes<'_>,
) -> CompressionResult<Vec<u8>>;
}
#[derive(Debug, Default, Clone, Copy)]
pub(crate) struct DefaultCompressionCodec;
impl CompressionCodec for DefaultCompressionCodec {
fn compress(&self, algo: CompressionAlgo, bytes: &[u8]) -> CompressionResult<Vec<u8>> {
match algo {
CompressionAlgo::Zstd => zstd::compress(bytes),
}
}
fn decompress(
&self,
algo: CompressionAlgo,
stored: StoredCompressedBytes<'_>,
) -> CompressionResult<Vec<u8>> {
match algo {
CompressionAlgo::Zstd => zstd::decompress(stored),
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct StoredCompressedBytes<'a> {
pub(crate) bytes: &'a [u8],
pub(crate) uncompressed_len: usize,
}
mod zstd {
use super::{CompressionAlgo, CompressionError, CompressionResult, StoredCompressedBytes};
const LEVEL: i32 = 3;
pub(super) fn compress(bytes: &[u8]) -> CompressionResult<Vec<u8>> {
::zstd::bulk::compress(bytes, LEVEL).map_err(|source| CompressionError::Compress {
algo: CompressionAlgo::Zstd,
source,
})
}
pub(super) fn decompress(stored: StoredCompressedBytes<'_>) -> CompressionResult<Vec<u8>> {
::zstd::bulk::decompress(stored.bytes, stored.uncompressed_len).map_err(|source| {
CompressionError::Decompress {
algo: CompressionAlgo::Zstd,
source,
}
})
}
}