axone_objectarium/
compress.rs

1use std::io;
2
3use enum_iterator::Sequence;
4use lzma_rs;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use snap;
8use thiserror::Error;
9
10/// CompressionAlgorithm is an enumeration that defines the different compression algorithms
11/// supported for compressing the content of objects.
12#[derive(Serialize, Copy, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Sequence)]
13pub enum CompressionAlgorithm {
14    /// Represents the "No compression" algorithm.
15    Passthrough,
16    /// Represents the Snappy algorithm.
17    Snappy,
18    /// Represents the LZMA algorithm.
19    Lzma,
20}
21
22impl CompressionAlgorithm {
23    /// compress returns the compressed data using the given algorithm.
24    pub fn compress(&self, data: &[u8]) -> Result<Vec<u8>, CompressionError> {
25        let compressor = match self {
26            CompressionAlgorithm::Passthrough => passthrough,
27            CompressionAlgorithm::Snappy => snappy_compress,
28            CompressionAlgorithm::Lzma => lzma_compress,
29        };
30        compressor(data)
31    }
32
33    /// decompress returns the decompressed data using the given algorithm.
34    /// The data must be compressed using the same algorithm.
35    pub fn decompress(&self, data: &[u8]) -> Result<Vec<u8>, CompressionError> {
36        let decompressor = match self {
37            CompressionAlgorithm::Passthrough => passthrough,
38            CompressionAlgorithm::Snappy => snappy_decompress,
39            CompressionAlgorithm::Lzma => lzma_decompress,
40        };
41        decompressor(data)
42    }
43}
44
45#[derive(Error, Debug, PartialEq, Eq)]
46pub enum CompressionError {
47    #[error("{0}")]
48    Error(String),
49}
50
51impl From<io::Error> for CompressionError {
52    fn from(err: io::Error) -> Self {
53        CompressionError::Error(err.to_string())
54    }
55}
56
57impl From<lzma_rs::error::Error> for CompressionError {
58    fn from(err: lzma_rs::error::Error) -> Self {
59        CompressionError::Error(err.to_string())
60    }
61}
62
63/// pass_through returns the data as is.
64#[inline]
65#[allow(clippy::unnecessary_wraps)]
66fn passthrough(data: &[u8]) -> Result<Vec<u8>, CompressionError> {
67    Ok(data.to_vec())
68}
69
70// snappy_compress returns the Snappy compressed data.
71#[inline]
72fn snappy_compress(data: &[u8]) -> Result<Vec<u8>, CompressionError> {
73    let mut reader = io::Cursor::new(data);
74    let mut writer = Vec::new();
75    {
76        let mut snappy_writer = snap::write::FrameEncoder::new(&mut writer);
77        io::copy(&mut reader, &mut snappy_writer)?;
78    }
79    Ok(writer)
80}
81
82// snappy_decompress returns the Snappy decompressed data.
83#[inline]
84fn snappy_decompress(data: &[u8]) -> Result<Vec<u8>, CompressionError> {
85    let reader = io::Cursor::new(data);
86    let mut snappy_reader = snap::read::FrameDecoder::new(reader);
87    let mut writer = Vec::new();
88    io::copy(&mut snappy_reader, &mut writer)?;
89    Ok(writer)
90}
91
92// lzma_compress returns the LZMA compressed data.
93#[inline]
94fn lzma_compress(data: &[u8]) -> Result<Vec<u8>, CompressionError> {
95    let mut reader = io::Cursor::new(data);
96    let mut writer = Vec::new();
97    lzma_rs::lzma_compress(&mut reader, &mut writer)?;
98    Ok(writer)
99}
100
101// lzma_decompress returns the LZMA decompressed data.
102#[inline]
103fn lzma_decompress(data: &[u8]) -> Result<Vec<u8>, CompressionError> {
104    let mut reader = io::Cursor::new(data);
105    let mut writer = Vec::new();
106    lzma_rs::lzma_decompress(&mut reader, &mut writer)?;
107    Ok(writer)
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_from_io_decompress_error() {
116        let cases = vec![
117            (
118                std::io::Error::new(
119                    std::io::ErrorKind::InvalidData,
120                    "the expected decompressed size differs, actual 998, expected 1000",
121                ),
122                CompressionError::Error(
123                    "the expected decompressed size differs, actual 998, expected 1000".to_string(),
124                ),
125            ),
126            (
127                std::io::Error::new(
128                    std::io::ErrorKind::InvalidData,
129                    lzma_rs::error::Error::IoError(std::io::Error::new(
130                        std::io::ErrorKind::InvalidData,
131                        "the expected decompressed size differs, actual 998, expected 1000",
132                    )),
133                ),
134                CompressionError::Error(
135                    "io error: the expected decompressed size differs, actual 998, expected 1000"
136                        .to_string(),
137                ),
138            ),
139        ];
140
141        for (error, expected_error) in cases {
142            let compression_err = CompressionError::from(error);
143
144            assert_eq!(compression_err, expected_error);
145        }
146    }
147}