pedronauck_data_parser/
compression_strategies.rs

1pub use async_compression::{
2    tokio::write as compression_encoders_and_decoders,
3    Level as CompressionLevel,
4};
5use tokio::io::AsyncWriteExt;
6
7use crate::CompressionError;
8
9/// A private module used to seal the `CompressionStrategy` trait. This ensures
10/// that the trait cannot be implemented outside of this module.
11mod private {
12    pub trait Sealed {}
13}
14
15/// The `CompressionStrategy` trait defines the interface for compression and decompression strategies.
16/// It is sealed to restrict external implementations.
17///
18/// # Requirements
19/// Implementations must:
20/// - Provide a name for the strategy.
21/// - Implement asynchronous methods for compressing and decompressing data.
22///
23/// # Associated Types
24/// - `name` - Returns the name of the compression strategy.
25/// - `compress` - Compresses the provided data asynchronously.
26/// - `decompress` - Decompresses the provided data asynchronously.
27#[async_trait::async_trait]
28pub trait CompressionStrategy: private::Sealed + Sync + Send {
29    /// Returns the name of the compression strategy.
30    fn name(&self) -> &'static str;
31
32    /// Compresses the provided data asynchronously.
33    ///
34    /// # Arguments
35    ///
36    /// * `uncompressed` - A slice of bytes representing the data to be compressed.
37    ///
38    /// # Returns
39    ///
40    /// A `Result` containing a `Vec<u8>` of the compressed data or a `CompressionError` if compression fails.
41    async fn compress(
42        &self,
43        uncompressed: &[u8],
44    ) -> Result<Vec<u8>, CompressionError>;
45
46    /// Decompresses the provided data asynchronously.
47    ///
48    /// # Arguments
49    ///
50    /// * `compressed` - A slice of bytes representing the data to be decompressed.
51    ///
52    /// # Returns
53    ///
54    /// A `Result` containing a `Vec<u8>` of the decompressed data or a `CompressionError` if decompression fails.
55    async fn decompress(
56        &self,
57        compressed: &[u8],
58    ) -> Result<Vec<u8>, CompressionError>;
59}
60
61/// A macro to define a new compression strategy by implementing the `CompressionStrategy` trait.
62///
63/// # Parameters
64/// - `$name`: The name of the compression strategy (typically a unit struct).
65/// - `$compression_type`: The compression type (e.g., ZLib, Gzip, Brotli, Bz, Lzma, Deflate, Zstd).
66/// - `$compression_level`: The compression level to be used (using `CompressionLevel` enum).
67///
68/// # Example
69/// struct TestCompressionStrategy;
70/// define_compression_strategy!(TestCompressionStrategy, Zlib, CompressionLevel::Fastest);
71macro_rules! define_compression_strategy {
72    ($name:ident, $compression_type:ident, $compression_level:ty) => {
73        impl private::Sealed for $name {}
74
75        #[async_trait::async_trait]
76        impl CompressionStrategy for $name {
77            fn name(&self) -> &'static str {
78                stringify!($name)
79            }
80
81            async fn compress(&self, uncompressed: &[u8]) -> Result<Vec<u8>, CompressionError> {
82                paste::paste! {
83                    let mut encoder = compression_encoders_and_decoders::[<$compression_type Encoder>]::with_quality(Vec::new(), $compression_level);
84                    encoder
85                        .write_all(uncompressed)
86                        .await
87                        .map_err(|e| CompressionError::[<$compression_type>](e))?;
88                    encoder
89                        .shutdown()
90                        .await
91                        .map_err(|e| CompressionError::[<$compression_type>](e))?;
92                    Ok(encoder.into_inner())
93                }
94            }
95
96            async fn decompress(&self, compressed: &[u8]) -> Result<Vec<u8>, CompressionError> {
97                paste::paste! {
98                    let mut decoder = compression_encoders_and_decoders::[<$compression_type Decoder>]::new(Vec::new());
99                    decoder
100                        .write_all(compressed)
101                        .await
102                        .map_err(|e| CompressionError::[<$compression_type>](e))?;
103                    decoder
104                        .shutdown()
105                        .await
106                        .map_err(|e| CompressionError::[<$compression_type>](e))?;
107                    Ok(decoder.into_inner())
108                }
109            }
110        }
111    };
112}
113
114#[cfg(feature = "zlib")]
115#[derive(Clone)]
116pub struct ZlibCompressionStrategy;
117#[cfg(feature = "zlib")]
118define_compression_strategy!(
119    ZlibCompressionStrategy,
120    Zlib,
121    CompressionLevel::Fastest
122);
123
124#[cfg(feature = "gzip")]
125#[derive(Clone)]
126pub struct GzipCompressionStrategy;
127#[cfg(feature = "gzip")]
128define_compression_strategy!(
129    GzipCompressionStrategy,
130    Gzip,
131    CompressionLevel::Fastest
132);
133
134#[cfg(feature = "brotli")]
135#[derive(Clone)]
136pub struct BrotliCompressionStrategy;
137#[cfg(feature = "brotli")]
138define_compression_strategy!(
139    BrotliCompressionStrategy,
140    Brotli,
141    CompressionLevel::Fastest
142);
143
144#[cfg(feature = "bzip2")]
145#[derive(Clone)]
146pub struct BzCompressionStrategy;
147#[cfg(feature = "bzip2")]
148define_compression_strategy!(
149    BzCompressionStrategy,
150    Bz,
151    CompressionLevel::Fastest
152);
153
154#[cfg(feature = "lzma")]
155#[derive(Clone)]
156pub struct LzmaCompressionStrategy;
157#[cfg(feature = "lzma")]
158define_compression_strategy!(
159    LzmaCompressionStrategy,
160    Lzma,
161    CompressionLevel::Fastest
162);
163
164#[cfg(feature = "deflate")]
165#[derive(Clone)]
166pub struct DeflateCompressionStrategy;
167#[cfg(feature = "deflate")]
168define_compression_strategy!(
169    DeflateCompressionStrategy,
170    Deflate,
171    CompressionLevel::Fastest
172);
173
174#[cfg(feature = "zstd")]
175#[derive(Clone)]
176pub struct ZstdCompressionStrategy;
177#[cfg(feature = "zstd")]
178define_compression_strategy!(
179    ZstdCompressionStrategy,
180    Zstd,
181    CompressionLevel::Fastest
182);
183
184use std::sync::Arc;
185
186lazy_static::lazy_static! {
187    pub static ref DEFAULT_COMPRESSION_STRATEGY: Arc<dyn CompressionStrategy> = Arc::new(ZstdCompressionStrategy);
188}
189
190lazy_static::lazy_static! {
191    pub static ref ALL_COMPRESSION_STRATEGIES: Vec<Arc<dyn CompressionStrategy>> = vec![
192        #[cfg(feature = "zlib")]
193        Arc::new(ZlibCompressionStrategy),
194        #[cfg(feature = "gzip")]
195        Arc::new(GzipCompressionStrategy),
196        #[cfg(feature = "brotli")]
197        Arc::new(BrotliCompressionStrategy),
198        #[cfg(feature = "bzip2")]
199        Arc::new(BzCompressionStrategy),
200        #[cfg(feature = "lzma")]
201        Arc::new(LzmaCompressionStrategy),
202        #[cfg(feature = "deflate")]
203        Arc::new(DeflateCompressionStrategy),
204        #[cfg(feature = "zstd")]
205        Arc::new(ZstdCompressionStrategy),
206    ];
207}