#![doc = include_str!("../README.md")]
mod compression_strategies;
mod error;
use std::{fmt::Debug, sync::Arc};
pub use compression_strategies::*;
pub use crate::error::{CompressionError, Error, SerdeError};
#[derive(Debug, Clone, strum::EnumIter, strum_macros::Display)]
pub enum SerializationType {
#[strum(serialize = "bincode")]
Bincode,
#[strum(serialize = "postcard")]
Postcard,
#[strum(serialize = "json")]
Json,
}
pub trait DataParseable:
serde::Serialize + serde::de::DeserializeOwned + Clone + Send + Sync + Debug
{
}
impl<
T: serde::Serialize
+ serde::de::DeserializeOwned
+ Clone
+ Send
+ Sync
+ Debug,
> DataParseable for T
{
}
#[derive(Clone)]
pub struct DataParser {
compression_strategy: Option<Arc<dyn CompressionStrategy>>,
pub serialization_type: SerializationType,
}
impl Default for DataParser {
fn default() -> Self {
Self {
compression_strategy: None,
serialization_type: SerializationType::Json,
}
}
}
impl DataParser {
pub fn with_compression_strategy(
mut self,
compression_strategy: &Arc<dyn CompressionStrategy>,
) -> Self {
self.compression_strategy = Some(compression_strategy.clone());
self
}
pub fn with_serialization_type(
mut self,
serialization_type: SerializationType,
) -> Self {
self.serialization_type = serialization_type;
self
}
pub async fn encode<T: DataParseable>(
&self,
data: &T,
) -> Result<Vec<u8>, Error> {
let serialized_data = self.serialize(data).await?;
Ok(match &self.compression_strategy {
Some(strategy) => strategy.compress(&serialized_data[..]).await?,
None => serialized_data,
})
}
pub async fn serialize<T: DataParseable>(
&self,
raw_data: &T,
) -> Result<Vec<u8>, Error> {
match self.serialization_type {
SerializationType::Bincode => bincode::serialize(&raw_data)
.map_err(|e| Error::Serde(SerdeError::Bincode(*e))),
SerializationType::Postcard => postcard::to_allocvec(&raw_data)
.map_err(|e| Error::Serde(SerdeError::Postcard(e))),
SerializationType::Json => serde_json::to_vec(&raw_data)
.map_err(|e| Error::Serde(SerdeError::Json(e))),
}
}
pub async fn decode<T: DataParseable>(
&self,
data: &[u8],
) -> Result<T, Error> {
let data = match &self.compression_strategy {
Some(strategy) => strategy.decompress(data).await?,
None => data.to_vec(),
};
let decoded_data = self.deserialize(&data[..]).await?;
Ok(decoded_data)
}
pub async fn deserialize<'a, T: serde::Deserialize<'a>>(
&self,
raw_data: &'a [u8],
) -> Result<T, Error> {
match self.serialization_type {
SerializationType::Bincode => bincode::deserialize(raw_data)
.map_err(|e| Error::Serde(SerdeError::Bincode(*e))),
SerializationType::Postcard => postcard::from_bytes(raw_data)
.map_err(|e| Error::Serde(SerdeError::Postcard(e))),
SerializationType::Json => serde_json::from_slice(raw_data)
.map_err(|e| Error::Serde(SerdeError::Json(e))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
struct TestData {
field: String,
}
#[tokio::test]
async fn test_encode_decode() {
let parser = DataParser::default();
let original_data = TestData {
field: "test".to_string(),
};
let encoded = parser.encode(&original_data).await.unwrap();
let decoded: TestData = parser.decode(&encoded).await.unwrap();
assert_eq!(original_data, decoded);
}
#[tokio::test]
async fn test_serialization_types() {
let data = TestData {
field: "test".to_string(),
};
for serialization_type in [
SerializationType::Bincode,
SerializationType::Postcard,
SerializationType::Json,
] {
let parser = DataParser::default()
.with_serialization_type(serialization_type);
let encoded = parser.encode(&data).await.unwrap();
let decoded: TestData = parser.decode(&encoded).await.unwrap();
assert_eq!(data, decoded);
}
}
#[tokio::test]
async fn test_compression_strategies() {
let data = TestData {
field: "test".to_string(),
};
let compression_strategies: Vec<Arc<dyn CompressionStrategy>> = vec![
Arc::new(ZLibCompressionStrategy),
#[cfg(feature = "bench-helpers")]
Arc::new(GzipCompressionStrategy),
#[cfg(feature = "bench-helpers")]
Arc::new(BrotliCompressionStrategy),
];
for strategy in compression_strategies {
let parser =
DataParser::default().with_compression_strategy(&strategy);
let encoded = parser.encode(&data).await.unwrap();
let decoded: TestData = parser.decode(&encoded).await.unwrap();
assert_eq!(data, decoded);
}
}
}