Skip to main content

network_protocol/core/
serialization.rs

1//! # Serialization Formats
2//!
3//! This module provides abstraction over multiple serialization formats for protocol messages.
4//! Supports bincode (default), JSON (debugging/interop), and MessagePack (compact encoding).
5//!
6//! ## Features
7//! - **Multiple formats**: Bincode, JSON, MessagePack with automatic format detection
8//! - **Zero-copy where possible**: Direct byte manipulation for performance-critical paths
9//! - **Format metadata**: Optional format byte prefix for automatic detection
10//! - **Human-readable options**: JSON with pretty-printing for debugging
11//! - **Compact encoding**: MessagePack for bandwidth-constrained scenarios
12//!
13//! ## Performance Characteristics
14//! - **Bincode**: ~100-200ns (fastest, binary)
15//! - **MessagePack**: ~150-300ns (compact, binary)
16//! - **JSON**: ~500-1000ns (human-readable, text)
17//!
18//! ## Usage
19//! ```ignore
20//! use network_protocol::codec::SerializationFormat;
21//!
22//! // Default bincode
23//! let bytes = bincode::serialize(&message)?;
24//!
25//! // JSON for debugging
26//! let json_bytes = serde_json::to_vec(&message)?;
27//!
28//! // MessagePack for compact encoding
29//! let msgpack_bytes = rmp_serde::to_vec(&message)?;
30//! ```
31
32use serde::{Deserialize, Serialize};
33
34/// Supported serialization formats
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
36pub enum SerializationFormat {
37    /// Binary compact format (default, fastest)
38    #[default]
39    Bincode,
40    /// Human-readable JSON format (debugging, interop)
41    Json,
42    /// Compact binary format (MessagePack, efficient)
43    MessagePack,
44}
45
46impl SerializationFormat {
47    /// Get the format identifier byte for wire protocol
48    pub fn format_byte(self) -> u8 {
49        match self {
50            SerializationFormat::Bincode => 0x01,
51            SerializationFormat::Json => 0x02,
52            SerializationFormat::MessagePack => 0x03,
53        }
54    }
55
56    /// Detect format from identifier byte
57    pub fn from_byte(byte: u8) -> Option<Self> {
58        match byte {
59            0x01 => Some(SerializationFormat::Bincode),
60            0x02 => Some(SerializationFormat::Json),
61            0x03 => Some(SerializationFormat::MessagePack),
62            _ => None,
63        }
64    }
65
66    /// Get human-readable name
67    pub fn name(self) -> &'static str {
68        match self {
69            SerializationFormat::Bincode => "Bincode",
70            SerializationFormat::Json => "JSON",
71            SerializationFormat::MessagePack => "MessagePack",
72        }
73    }
74}
75
76/// Trait for types that support multiple serialization formats
77pub trait MultiFormat: Serialize + for<'de> Deserialize<'de> + Sized {
78    /// Serialize to bytes using the specified format
79    fn serialize_format(&self, format: SerializationFormat) -> crate::error::Result<Vec<u8>> {
80        match format {
81            SerializationFormat::Bincode => bincode::serialize(self)
82                .map_err(|e| crate::error::ProtocolError::SerializeError(e.to_string())),
83            SerializationFormat::Json => serde_json::to_vec(self)
84                .map_err(|e| crate::error::ProtocolError::SerializeError(e.to_string())),
85            SerializationFormat::MessagePack => rmp_serde::to_vec(self)
86                .map_err(|e| crate::error::ProtocolError::SerializeError(e.to_string())),
87        }
88    }
89
90    /// Serialize to bytes with format header
91    fn serialize_with_header(&self, format: SerializationFormat) -> crate::error::Result<Vec<u8>> {
92        let mut data = vec![format.format_byte()];
93        let mut payload = self.serialize_format(format)?;
94        data.append(&mut payload);
95        Ok(data)
96    }
97
98    /// Deserialize from bytes using the specified format
99    fn deserialize_format(data: &[u8], format: SerializationFormat) -> crate::error::Result<Self> {
100        match format {
101            SerializationFormat::Bincode => bincode::deserialize(data)
102                .map_err(|e| crate::error::ProtocolError::DeserializeError(e.to_string())),
103            SerializationFormat::Json => serde_json::from_slice(data)
104                .map_err(|e| crate::error::ProtocolError::DeserializeError(e.to_string())),
105            SerializationFormat::MessagePack => rmp_serde::from_slice(data)
106                .map_err(|e| crate::error::ProtocolError::DeserializeError(e.to_string())),
107        }
108    }
109
110    /// Deserialize from bytes with format header
111    fn deserialize_with_header(data: &[u8]) -> crate::error::Result<(Self, SerializationFormat)> {
112        if data.is_empty() {
113            return Err(crate::error::ProtocolError::DeserializeError(
114                "Empty data".to_string(),
115            ));
116        }
117
118        let format = SerializationFormat::from_byte(data[0]).ok_or_else(|| {
119            crate::error::ProtocolError::DeserializeError(format!(
120                "Unknown format byte: {}",
121                data[0]
122            ))
123        })?;
124
125        let value = Self::deserialize_format(&data[1..], format)?;
126        Ok((value, format))
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::protocol::message::Message;
134
135    #[test]
136    #[allow(clippy::expect_used)]
137    fn test_format_byte_roundtrip() {
138        for format in &[
139            SerializationFormat::Bincode,
140            SerializationFormat::Json,
141            SerializationFormat::MessagePack,
142        ] {
143            let byte = format.format_byte();
144            let recovered = SerializationFormat::from_byte(byte).expect("valid format byte");
145            assert_eq!(*format, recovered);
146        }
147    }
148
149    #[test]
150    fn test_format_names() {
151        assert_eq!(SerializationFormat::Bincode.name(), "Bincode");
152        assert_eq!(SerializationFormat::Json.name(), "JSON");
153        assert_eq!(SerializationFormat::MessagePack.name(), "MessagePack");
154    }
155
156    #[test]
157    fn test_default_format() {
158        assert_eq!(SerializationFormat::default(), SerializationFormat::Bincode);
159    }
160
161    #[test]
162    #[allow(clippy::expect_used)]
163    fn test_bincode_serialization() {
164        let msg = Message::Ping;
165        let bytes = bincode::serialize(&msg).expect("serialize");
166        let recovered: Message = bincode::deserialize(&bytes).expect("deserialize");
167        assert_eq!(msg, recovered);
168    }
169
170    #[test]
171    #[allow(clippy::expect_used)]
172    fn test_json_serialization() {
173        let msg = Message::Ping;
174        let bytes = serde_json::to_vec(&msg).expect("serialize");
175        let recovered: Message = serde_json::from_slice(&bytes).expect("deserialize");
176        assert_eq!(msg, recovered);
177    }
178
179    #[test]
180    #[allow(clippy::expect_used)]
181    fn test_messagepack_serialization() {
182        let msg = Message::Ping;
183        let bytes = rmp_serde::to_vec(&msg).expect("serialize");
184        let recovered: Message = rmp_serde::from_slice(&bytes).expect("deserialize");
185        assert_eq!(msg, recovered);
186    }
187
188    #[test]
189    #[allow(clippy::expect_used)]
190    fn test_format_sizes() {
191        let msg = Message::Ping;
192
193        let bincode_size = bincode::serialize(&msg).expect("bincode").len();
194        let json_size = serde_json::to_vec(&msg).expect("json").len();
195        let msgpack_size = rmp_serde::to_vec(&msg).expect("msgpack").len();
196
197        println!("Bincode: {bincode_size} bytes");
198        println!("JSON: {json_size} bytes");
199        println!("MessagePack: {msgpack_size} bytes");
200
201        // MessagePack should be more compact than JSON
202        assert!(msgpack_size < json_size);
203    }
204}