byte_lamination 0.1.2

Type-readable byte transformation wrappers, with CBOR and BARE serialisation and Zstd compression.
Documentation
use crate::{AutoDelaminate, AutoLaminate, ByteLamination};
use std::borrow::Cow;
use std::cmp::max;
use std::error::Error;
use std::marker::PhantomData;

/// Wrapper that performs Zstd (de)compression on the bytes.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Zstd<'a, T> {
    bytes: Cow<'a, [u8]>,
    _marker: PhantomData<T>,
}

impl<'a, T> From<Cow<'a, [u8]>> for Zstd<'a, T> {
    fn from(bytes: Cow<'a, [u8]>) -> Self {
        Self {
            bytes,
            _marker: PhantomData::default(),
        }
    }
}

impl<'a, T> Into<Cow<'a, [u8]>> for Zstd<'a, T> {
    fn into(self) -> Cow<'a, [u8]> {
        self.bytes
    }
}

impl<'a, T> ByteLamination<'a> for Zstd<'a, T> {
    fn as_cow_bytes(&self) -> Cow<'_, [u8]> {
        Cow::Borrowed(&self.bytes)
    }

    fn into_bytes(self) -> Vec<u8> {
        self.bytes.into_owned()
    }

    fn try_from_bytes(bytes: Cow<'a, [u8]>) -> Result<Self, Box<dyn Error>> {
        Ok(Self::from(bytes))
    }
}

/// Default Zstd compression level used for the AutoLaminate helper.
pub const DEFAULT_ZSTD_LEVEL: i32 = 12;

/// Default memory limit for Zstd decompression used for the AutoDelaminate helper (multiplier of
/// input size).
pub const DEFAULT_MEMORY_LIMIT_MULTIPLIER: usize = 16;
/// Default minimum memory limit for Zstd decompression used for the AutoDelaminate helper.
pub const DEFAULT_MEMORY_LIMIT_MINIMUM: usize = 1024 * 1024 * 1024;

impl<T> Zstd<'static, T> {
    pub fn compress(bytes: &[u8], level: i32) -> Result<Self, Box<dyn Error>> {
        let compresed_vec = zstd::bulk::compress(bytes, level)?;
        Ok(Self::from(Cow::Owned(compresed_vec)))
    }
}

impl<'a, T> Zstd<'a, T> {
    pub fn decompress(&self, memory_limit: usize) -> Result<Vec<u8>, Box<dyn Error>> {
        let decompressed_vec = zstd::bulk::decompress(&self.bytes, memory_limit)?;
        Ok(decompressed_vec)
    }
}

impl<'a, T: AutoLaminate<U> + ByteLamination<'a> + 'a, U> AutoLaminate<U> for Zstd<'a, T> {
    fn laminate(item: U) -> Result<Self, Box<dyn Error>> {
        let lamination = T::laminate(item)?;
        let bytes: Cow<'_, [u8]> = lamination.as_cow_bytes();
        Zstd::compress(&bytes, DEFAULT_ZSTD_LEVEL)
    }
}

impl<'a, T: AutoDelaminate<U> + ByteLamination<'a>, U> AutoDelaminate<U> for Zstd<'a, T> {
    fn delaminate(self) -> Result<U, Box<dyn Error>> {
        let memory_limit = max(
            DEFAULT_MEMORY_LIMIT_MINIMUM,
            DEFAULT_MEMORY_LIMIT_MULTIPLIER * self.bytes.len(),
        );
        let bytes = self.decompress(memory_limit)?;
        T::try_from_bytes(Cow::Owned(bytes))?.delaminate()
    }
}