use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SerializationFormat {
#[default]
Bincode,
Json,
MessagePack,
}
impl SerializationFormat {
pub fn format_byte(self) -> u8 {
match self {
SerializationFormat::Bincode => 0x01,
SerializationFormat::Json => 0x02,
SerializationFormat::MessagePack => 0x03,
}
}
pub fn from_byte(byte: u8) -> Option<Self> {
match byte {
0x01 => Some(SerializationFormat::Bincode),
0x02 => Some(SerializationFormat::Json),
0x03 => Some(SerializationFormat::MessagePack),
_ => None,
}
}
pub fn name(self) -> &'static str {
match self {
SerializationFormat::Bincode => "Bincode",
SerializationFormat::Json => "JSON",
SerializationFormat::MessagePack => "MessagePack",
}
}
}
pub trait MultiFormat: Serialize + for<'de> Deserialize<'de> + Sized {
fn serialize_format(&self, format: SerializationFormat) -> crate::error::Result<Vec<u8>> {
match format {
SerializationFormat::Bincode => bincode::serialize(self)
.map_err(|e| crate::error::ProtocolError::SerializeError(e.to_string())),
SerializationFormat::Json => serde_json::to_vec(self)
.map_err(|e| crate::error::ProtocolError::SerializeError(e.to_string())),
SerializationFormat::MessagePack => rmp_serde::to_vec(self)
.map_err(|e| crate::error::ProtocolError::SerializeError(e.to_string())),
}
}
fn serialize_with_header(&self, format: SerializationFormat) -> crate::error::Result<Vec<u8>> {
let mut data = vec![format.format_byte()];
let mut payload = self.serialize_format(format)?;
data.append(&mut payload);
Ok(data)
}
fn deserialize_format(data: &[u8], format: SerializationFormat) -> crate::error::Result<Self> {
match format {
SerializationFormat::Bincode => bincode::deserialize(data)
.map_err(|e| crate::error::ProtocolError::DeserializeError(e.to_string())),
SerializationFormat::Json => serde_json::from_slice(data)
.map_err(|e| crate::error::ProtocolError::DeserializeError(e.to_string())),
SerializationFormat::MessagePack => rmp_serde::from_slice(data)
.map_err(|e| crate::error::ProtocolError::DeserializeError(e.to_string())),
}
}
fn deserialize_with_header(data: &[u8]) -> crate::error::Result<(Self, SerializationFormat)> {
if data.is_empty() {
return Err(crate::error::ProtocolError::DeserializeError(
"Empty data".to_string(),
));
}
let format = SerializationFormat::from_byte(data[0]).ok_or_else(|| {
crate::error::ProtocolError::DeserializeError(format!(
"Unknown format byte: {}",
data[0]
))
})?;
let value = Self::deserialize_format(&data[1..], format)?;
Ok((value, format))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::message::Message;
#[test]
#[allow(clippy::expect_used)]
fn test_format_byte_roundtrip() {
for format in &[
SerializationFormat::Bincode,
SerializationFormat::Json,
SerializationFormat::MessagePack,
] {
let byte = format.format_byte();
let recovered = SerializationFormat::from_byte(byte).expect("valid format byte");
assert_eq!(*format, recovered);
}
}
#[test]
fn test_format_names() {
assert_eq!(SerializationFormat::Bincode.name(), "Bincode");
assert_eq!(SerializationFormat::Json.name(), "JSON");
assert_eq!(SerializationFormat::MessagePack.name(), "MessagePack");
}
#[test]
fn test_default_format() {
assert_eq!(SerializationFormat::default(), SerializationFormat::Bincode);
}
#[test]
#[allow(clippy::expect_used)]
fn test_bincode_serialization() {
let msg = Message::Ping;
let bytes = bincode::serialize(&msg).expect("serialize");
let recovered: Message = bincode::deserialize(&bytes).expect("deserialize");
assert_eq!(msg, recovered);
}
#[test]
#[allow(clippy::expect_used)]
fn test_json_serialization() {
let msg = Message::Ping;
let bytes = serde_json::to_vec(&msg).expect("serialize");
let recovered: Message = serde_json::from_slice(&bytes).expect("deserialize");
assert_eq!(msg, recovered);
}
#[test]
#[allow(clippy::expect_used)]
fn test_messagepack_serialization() {
let msg = Message::Ping;
let bytes = rmp_serde::to_vec(&msg).expect("serialize");
let recovered: Message = rmp_serde::from_slice(&bytes).expect("deserialize");
assert_eq!(msg, recovered);
}
#[test]
#[allow(clippy::expect_used)]
fn test_format_sizes() {
let msg = Message::Ping;
let bincode_size = bincode::serialize(&msg).expect("bincode").len();
let json_size = serde_json::to_vec(&msg).expect("json").len();
let msgpack_size = rmp_serde::to_vec(&msg).expect("msgpack").len();
println!("Bincode: {bincode_size} bytes");
println!("JSON: {json_size} bytes");
println!("MessagePack: {msgpack_size} bytes");
assert!(msgpack_size < json_size);
}
}